dsh-metaphysics 0.64.0 → 0.66.0

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.
@@ -362,7 +362,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
362
362
  <div class="wrap">
363
363
  <header>
364
364
  <div class="brand-row">
365
- <div class="brand"><span class="seal" aria-hidden="true">命</span><span class="brand-txt">命理排盘<span class="ver">v0.64.0</span><small>八字 · 紫微 · 六爻 · 梅花 · 六壬 · 奇门 · 合盘 · 塔罗 全部在本机计算</small></span></div>
365
+ <div class="brand"><span class="seal" aria-hidden="true">命</span><span class="brand-txt">命理排盘<span class="ver">v0.66.0</span><small>八字 · 紫微 · 六爻 · 梅花 · 六壬 · 奇门 · 合盘 · 塔罗 全部在本机计算</small></span></div>
366
366
  <div class="view-switch">
367
367
  <span class="hint">熟练了可以换回平铺</span>
368
368
  <span class="seg" role="group" aria-label="首屏样式">
@@ -653,7 +653,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
653
653
  <h3>对拍:把典籍当期望值</h3>
654
654
  <ul><li>六爻:京房八宫卦序与世应,64 卦全表;旬空六旬表</li><li>六壬:日干寄宫、中气换将;紫微:纳音定五行局、紫微天府相照</li><li>卦例:《增删卜易》9 条 +《卜筮正宗》128 条(93 成对 + 35 静卦),核本变卦 / 动爻数 / 卦宫 / 缺爻 / 旬空</li><li>奇门:20 个时辰 × 9 宫,核四柱 / 局数 / 值符 / 值使 / 星门神 / 天盘干</li></ul>
655
655
  <h3>工程验证</h3>
656
- <ul><li>测试:164 个测试文件 / 8891 条断言;七道闸(回归 · 断言基线 · 测试可失败自检 · HTML 注入面 · 工程约定 · 产物宿主形态 · 随机扫射)</li><li>可复现:seed / asOf 显式;同一输入同一输出</li><li>口径:晚子时、真太阳时、奇门三流派、闰月归月都可切换,并有字段级差异对照</li></ul>
656
+ <ul><li>测试:167 个测试文件 / 9074 条断言;七道闸(回归 · 断言基线 · 测试可失败自检 · HTML 注入面 · 工程约定 · 产物宿主形态 · 随机扫射)</li><li>可复现:seed / asOf 显式;同一输入同一输出</li><li>口径:晚子时、真太阳时、奇门三流派、闰月归月都可切换,并有字段级差异对照</li></ul>
657
657
 
658
658
  <h3>已知边界(不猜)</h3>
659
659
  <ul><li>六爻应期(具体哪天应验)不做;卦例只录动爻个数,不录爻位</li><li>财富/健康只给结构判断与档位,不给金额区间、不作诊断</li><li>紫微飞星已做「单一支化逐宫追到成环」与「三方四正 16 条按落宫归堆」,但都未叠大限流年四化、不折吉凶;八字交运日已能算到日/时,但未与第三方排盘案例批量对拍</li></ul>
@@ -1241,6 +1241,12 @@ const CALIBER_SWITCHES = {
1241
1241
  阳武府相: '庚干 阳武府相', 阳武同相: '庚干 阳武同相', 巨阳武昌: '辛干 巨阳武昌',
1242
1242
  梁紫府武: '壬干 梁紫府武', 梁紫相武: '壬干 梁紫相武', 破巨阳贪: '癸干 破巨阳贪',
1243
1243
  }),
1244
+ ziweiSchoolSwitch('jieluSchool', '截路空亡:照常 / 不排(中州派)', '截路空亡', 'off', 'zhongzhou', {
1245
+ off: '照常(截空/副截/旬空/副旬)', zhongzhou: '不排(中州派)',
1246
+ }),
1247
+ ziweiSchoolSwitch('suipoSchool', '岁前十二神:照常 / 大耗改岁破(中州派)', '岁前十二神', 'off', 'zhongzhou', {
1248
+ off: '照常(大耗)', zhongzhou: '大耗改岁破',
1249
+ }),
1244
1250
  ],
1245
1251
  qimen: [
1246
1252
  {
@@ -2746,7 +2752,10 @@ const YUEDE_HE = { 丙: '辛', 壬: '丁', 甲: '己', 庚: '乙' };
2746
2752
  const FUXING = {
2747
2753
  甲: ['寅', '子'], 丙: ['寅', '子'],
2748
2754
  乙: ['卯', '丑'], 癸: ['卯', '丑'],
2749
- 丁: ['酉'], 戊: ['申'], 己: ['未'], 庚: ['午'], 辛: ['巳'], 壬: ['辰'],
2755
+ // 丁取亥:本表所引《甲丙相邀入虎乡歌》原文作「戊猴己未丁宜亥」。
2756
+ // 旧表写丁酉,与自家所引歌诀对不上——酉是「丁酉」这个干支的干,不是亥的形讹,
2757
+ // 属后世把干支名误当查法所致。改从歌诀,并保留旧说于 rule 文本备查。
2758
+ 丁: ['亥'], 戊: ['申'], 己: ['未'], 庚: ['午'], 辛: ['巳'], 壬: ['辰'],
2750
2759
  };
2751
2760
  // 天官贵人(通行表,流派有异)
2752
2761
  const TIANGUAN = { 甲: ['未'], 乙: ['辰'], 丙: ['巳'], 丁: ['酉'], 戊: ['戌'], 己: ['卯'], 庚: ['亥'], 辛: ['申'], 壬: ['午'], 癸: ['寅'] };
@@ -2781,10 +2790,16 @@ const WANGSHEN = { 寅午戌: ['巳'], 申子辰: ['亥'], 巳酉丑: ['申'],
2781
2790
  const JINYU = { 甲: ['辰'], 乙: ['巳'], 丙: ['未'], 丁: ['申'], 戊: ['未'], 己: ['申'], 庚: ['戌'], 辛: ['亥'], 壬: ['丑'], 癸: ['寅'] };
2782
2791
  // 禄神
2783
2792
  const LUSHEN = { 甲: ['寅'], 乙: ['卯'], 丙: ['巳'], 丁: ['午'], 戊: ['巳'], 己: ['午'], 庚: ['申'], 辛: ['酉'], 壬: ['亥'], 癸: ['子'] };
2784
- // 羊刃:禄前一位(阳干论刃为主)
2785
- const YANGREN = { 甲: ['卯'], 乙: ['辰'], 丙: ['午'], 丁: ['未'], 戊: ['午'], 己: ['未'], 庚: ['酉'], 辛: ['戌'], 壬: ['子'], 癸: ['丑'] };
2786
- // 飞刃:羊刃六冲
2787
- const FEIREN = { 甲: ['酉'], 乙: ['戌'], 丙: ['子'], 丁: ['丑'], 戊: ['子'], 己: ['丑'], 庚: ['卯'], 辛: ['辰'], 壬: ['午'], 癸: ['未'] };
2793
+ // 羊刃(DeepSeek 派):帝旺本位,阴阳干都取「禄后一位」——即阳干帝旺、阴干亦帝旺。
2794
+ // 阳干:甲卯 丙午 戊午 庚酉 壬子(十二长生帝旺位,历代无争议)。
2795
+ // 阴干:乙寅 丁巳 己巳 辛申 癸亥(阴干逆行长生,帝旺在其长生进位,非禄前)。
2796
+ // 旧表把阴干取成禄前一位(乙辰 丁未 己未 辛戌 癸丑),那一位其实是阴干的「衰」;
2797
+ // 说「乙刃在辰」是宋以后禄命家把阳干式硬套到阴干的结果,与十二长生自相矛盾。
2798
+ const YANGREN = { 甲: ['卯'], 乙: ['寅'], 丙: ['午'], 丁: ['巳'], 戊: ['午'], 己: ['巳'], 庚: ['酉'], 辛: ['申'], 壬: ['子'], 癸: ['亥'] };
2799
+ // 羊刃六冲(由 YANGREN 逐干取对冲,勿手写,免两表再度走散)
2800
+ const FEIREN = Object.fromEntries(
2801
+ Object.entries(YANGREN).map(([stem, arr]) => [stem, arr.map((b) => BRANCHES[(BRANCHES.indexOf(b) + 6) % 12])]),
2802
+ );
2788
2803
  // 流霞(通行表)
2789
2804
  const LIUXIA = { 甲: ['酉'], 乙: ['戌'], 丙: ['未'], 丁: ['申'], 戊: ['巳'], 己: ['午'], 庚: ['辰'], 辛: ['卯'], 壬: ['亥'], 癸: ['寅'] };
2790
2805
 
@@ -2801,11 +2816,40 @@ const TIAN_SHE = { 春: '戊寅', 夏: '甲午', 秋: '戊申', 冬: '甲子' };
2801
2816
  const SIFEI = { 春: ['庚申', '辛酉'], 夏: ['壬子', '癸亥'], 秋: ['甲寅', '乙卯'], 冬: ['丙午', '丁巳'] };
2802
2817
  // 童子煞:春秋生见寅子,冬夏生见卯未辰
2803
2818
  const TONGZI = { 春: ['寅', '子'], 秋: ['寅', '子'], 夏: ['卯', '未', '辰'], 冬: ['卯', '未', '辰'] };
2819
+ // 童子煞(纳音法,与季节法并行):金木命见午卯、水火命见酉戌、土命见辰巳
2820
+ // 口诀作「金木马卯合,水火鸡犬多,土命逢辰巳」——马=午、鸡=酉、犬=戌。
2821
+ // 旧实现只做季节法,把纳音法写作「流派多歧,仅作参考」,等于漏了一半查法。
2822
+ const TONGZI_NAYIN = { 金: ['午', '卯'], 木: ['午', '卯'], 水: ['酉', '戌'], 火: ['酉', '戌'], 土: ['辰', '巳'] };
2804
2823
  // 三奇贵人(须依序相连)
2805
2824
  const SANQI = ['甲戊庚', '乙丙丁', '壬癸辛'];
2806
2825
  // 金神:时柱见之
2807
2826
  const JINSHEN = ['乙丑', '己巳', '癸酉'];
2808
2827
 
2828
+ // ── 问真神煞大全补缺批(0.66):口径全部按《问真神煞大全》55 章实读钉死 ──
2829
+
2830
+ // 天德合:天德之干五合/支六合(寅月天德丁→壬合…)
2831
+ const TIANDE_HE = { 寅: '壬', 卯: '巳', 辰: '丁', 巳: '丙', 午: '寅', 未: '己', 申: '戊', 酉: '亥', 戌: '辛', 亥: '庚', 子: '申', 丑: '乙' };
2832
+ // 血刃:按月支查支
2833
+ const XUEYREN = { 寅: '丑', 卯: '未', 辰: '寅', 巳: '申', 午: '卯', 未: '酉', 申: '辰', 酉: '戌', 戌: '巳', 亥: '亥', 子: '午', 丑: '子' };
2834
+ // 红艳煞:日干查支
2835
+ const HONGYAN = { 甲: ['午'], 乙: ['午'], 丙: ['寅'], 丁: ['未'], 戊: ['辰'], 己: ['辰'], 庚: ['戌'], 辛: ['酉'], 壬: ['子'], 癸: ['申'] };
2836
+ // 孤鸾煞:日柱
2837
+ const GUALUAN = ['甲寅', '乙巳', '丙午', '丁巳', '戊午', '戊申', '辛亥', '壬子'];
2838
+ // 十灵日:日柱
2839
+ const SHILING = ['甲辰', '乙亥', '丙辰', '丁酉', '戊午', '庚戌', '庚寅', '辛亥', '壬寅', '癸未'];
2840
+ // 八专:日柱
2841
+ const BAZHUAN = ['甲寅', '乙卯', '丁未', '戊戌', '己未', '庚申', '辛酉', '癸丑'];
2842
+ // 九丑:日柱
2843
+ const JIUCHOU = ['丁酉', '戊子', '戊午', '己卯', '己酉', '辛卯', '辛酉', '壬子', '壬午'];
2844
+ // 天转:寅卯辰月乙卯、巳午未月丙午、申酉戌月辛酉、亥子丑月壬子(日柱)
2845
+ const TIANZH = { 春: '乙卯', 夏: '丙午', 秋: '辛酉', 冬: '壬子' };
2846
+ // 地转:春辛卯、夏戊午、秋癸酉、冬丙子(日柱)
2847
+ const DIZH = { 春: '辛卯', 夏: '戊午', 秋: '癸酉', 冬: '丙子' };
2848
+ // 学堂(纳音长生查法,问真采用第一种):年纳音五行 → 支
2849
+ const XUETANG = { 金: '巳', 木: '亥', 水: '申', 土: '申', 火: '寅' };
2850
+ // 词馆(纳音临官查法):年纳音五行 → 支
2851
+ const CIGUAN = { 金: '申', 木: '寅', 水: '亥', 土: '亥', 火: '巳' };
2852
+
2809
2853
  // ── 规则工厂 ────────────────────────────────────────────────────
2810
2854
 
2811
2855
  /** 天干查支:天乙、太极、文昌、福星、天官、国印、天厨、禄神、羊刃、飞刃、金舆、流霞 */
@@ -2878,8 +2922,16 @@ const RULES = [
2878
2922
  const he = k && YUEDE[k] ? YUEDE_HE[YUEDE[k]] : null;
2879
2923
  return he ? [S(he)] : [];
2880
2924
  }),
2925
+ byMonth('天德合', '贵人', 'good',
2926
+ '天德之合:寅月丁德→壬合、卯月申→巳、辰月壬→丁、巳月辛→丙、午月亥→寅、未月甲→己、申月癸→戊、酉月寅→亥、戌月丙→辛、亥月乙→庚、子月巳→申、丑月庚→乙(月支查干或支;无天德时天德合同样起作用)',
2927
+ (ctx) => {
2928
+ const ch = TIANDE_HE[ctx.pillars.month.branch];
2929
+ if (!ch) return [];
2930
+ return STEMS_LIST.includes(ch) ? [S(ch)] : [T(ch)];
2931
+ }),
2881
2932
  byStem('福星贵人', '贵人', 'good',
2882
- '甲丙见寅子,乙癸见卯丑,丁见酉、戊见申、己见未、庚见午、辛见巳、壬见辰(年干、日干查四支)',
2933
+ '甲丙见寅子,乙癸见卯丑,戊见申、己见未、丁见亥、庚见午、辛见巳、壬见辰(年干、日干查四支)。'
2934
+ + '出自《甲丙相邀入虎乡歌》「戊猴己未丁宜亥」;旧本有作丁见酉者,与歌诀不合,本插件从歌诀',
2883
2935
  FUXING, ['year', 'day']),
2884
2936
  byStem('天官贵人', '贵人', 'good',
2885
2937
  '甲未、乙辰、丙巳、丁酉、戊戌、己卯、庚亥、辛申、壬午、癸寅(年干、日干查四支;此表流派有异)',
@@ -2940,26 +2992,61 @@ const RULES = [
2940
2992
  byStem('金舆', '婚姻', 'good',
2941
2993
  '禄前二位:甲龙乙蛇丙戊羊、丁己申宫庚犬乡、辛猪壬牛癸虎位(日干查四支)',
2942
2994
  JINYU, ['day']),
2995
+ byStem('红艳煞', '婚姻', 'neutral',
2996
+ '甲乙午、丙寅、丁未、戊己辰、庚戌、辛酉、壬子、癸申(日干查四支)',
2997
+ HONGYAN, ['day']),
2998
+ byPillarGz('孤鸾煞', '婚姻', 'bad',
2999
+ '日柱甲寅、乙巳、丙午、丁巳、戊午、戊申、辛亥、壬子(日柱见之;又名呻吟煞)',
3000
+ GUALUAN),
3001
+ // 勾绞煞:桃花六冲之位,阳男阴女命前三辰为勾、后三辰为绞;阴男阳女反之
3002
+ byRefBranch('勾绞煞', '婚姻', 'bad',
3003
+ '桃花六冲之位:子见卯酉、丑见辰戌、寅见巳亥……(年支查余三支;阳男阴女命前三辰为勾后三为绞,阴男阳女反之)',
3004
+ (ctx) => {
3005
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
3006
+ const qian = BRANCHES[(idx + 3) % 12];
3007
+ const hou = BRANCHES[(idx + 9) % 12];
3008
+ const gouxian = ctx.gender === 'male'
3009
+ ? ((idx % 2) === 0) // 阳男:命前为勾
3010
+ : ((idx % 2) === 1); // 阴女:命前为勾
3011
+ return gouxian
3012
+ ? [{ kind: 'branch', ch: qian, label: '勾' }, { kind: 'branch', ch: hou, label: '绞' }]
3013
+ : [{ kind: 'branch', ch: hou, label: '勾' }, { kind: 'branch', ch: qian, label: '绞' }];
3014
+ }),
3015
+ // 元辰:阳男阴女年支后一辰冲位、阴男阳女前一辰冲位(问真:阳男阴女子年未…阴男阳女子年巳…)
3016
+ byRefBranch('元辰', '凶煞', 'bad',
3017
+ '阳男阴女:子见未、丑见申、寅见酉……(年支次一位的对冲);阴男阳女:子见巳、丑见午……(年支退一位的对冲)(年支查余三支;又名毛头星、大耗)',
3018
+ (ctx) => {
3019
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
3020
+ const yangNan = ctx.gender === 'male'
3021
+ ? ((idx % 2) === 0)
3022
+ : ((idx % 2) === 1);
3023
+ const base = yangNan ? (idx + 1) % 12 : (idx + 11) % 12;
3024
+ return [T(BRANCHES[(base + 6) % 12])];
3025
+ }),
2943
3026
 
2944
3027
  // 性情
2945
3028
  byTrio('华盖', '性情', 'neutral',
2946
3029
  '寅午戌见戌、申子辰见辰、巳酉丑见丑、亥卯未见未(年支、日支双查)',
2947
3030
  HUAGAI),
2948
3031
  byStem('羊刃', '性情', 'bad',
2949
- '禄前一位:甲卯、乙辰、丙戊午、丁己未、庚酉、辛戌、壬子、癸丑(日干查四支;阳干论刃为主)',
3032
+ '帝旺之位:甲卯、乙寅、丙午、丁巳、戊午、己巳、庚酉、辛申、壬子、癸亥(日干查四支)。'
3033
+ + '旧本把阴干记作禄前一位(乙辰、丁未、己未、辛戌、癸丑),那一位实为阴干之「衰」,与十二长生自相矛盾,本插件不取',
2950
3034
  YANGREN, ['day']),
2951
3035
  byStem('飞刃', '性情', 'bad',
2952
- '羊刃六冲:甲酉、乙戌、丙子、丁丑、戊子、己丑、庚卯、辛辰、壬午、癸未(日干查四支)',
3036
+ '羊刃六冲(由羊刃位逐干对冲推得,不另立表):甲酉、乙申、丙子、丁亥、戊子、己亥、庚卯、辛寅、壬午、癸巳(日干查四支)',
2953
3037
  FEIREN, ['day']),
2954
3038
  byPillarGz('魁罡', '性情', 'neutral',
2955
3039
  '日柱庚辰、庚戌、壬辰、戊戌(日柱见之)',
2956
3040
  KUIGANG),
2957
3041
  byMonth('童子煞', '性情', 'neutral',
2958
- '春秋生见寅子,冬夏生见卯未辰(取季节口诀,查日支、时支;另有纳音五行一法,流派多歧,仅作参考)',
3042
+ '两法并行:①季节法——春秋生见寅子,冬夏生见卯未辰(查日支、时支);'
3043
+ + '②纳音法——金木命见午卯,水火命见酉戌,土命见辰巳(年柱纳音查日支、时支)。任一中即算',
2959
3044
  (ctx) => {
3045
+ const set = new Set();
2960
3046
  const season = SEASON[ctx.pillars.month.branch];
2961
- const list = season && TONGZI[season];
2962
- return list ? list.map(T) : [];
3047
+ for (const b of (season && TONGZI[season]) || []) set.add(b);
3048
+ for (const b of TONGZI_NAYIN[ctx.nayinElement] || []) set.add(b);
3049
+ return [...set].map(T);
2963
3050
  }, ['day', 'hour']),
2964
3051
  byPillarGz('六秀日', '性情', 'good',
2965
3052
  '日柱丙午、丁未、戊子、戊午、己丑、己未(日柱见之)',
@@ -2970,6 +3057,15 @@ const RULES = [
2970
3057
  byPillarGz('日贵', '性情', 'good',
2971
3058
  '日柱丁酉、丁亥、癸巳、癸卯(日柱见之)',
2972
3059
  RIGUI),
3060
+ byPillarGz('十灵日', '性情', 'good',
3061
+ '日柱甲辰、乙亥、丙辰、丁酉、戊午、庚戌、庚寅、辛亥、壬寅、癸未(日柱见之;「男带十灵好文章,女带十灵好衣裳」)',
3062
+ SHILING),
3063
+ byPillarGz('八专', '性情', 'neutral',
3064
+ '日柱甲寅、乙卯、丁未、戊戌、己未、庚申、辛酉、癸丑(日柱见之;干支同气坐禄或冠带)',
3065
+ BAZHUAN),
3066
+ byPillarGz('九丑', '性情', 'neutral',
3067
+ '日柱丁酉、戊子、戊午、己卯、己酉、辛卯、辛酉、壬子、壬午(日柱见之;「丑」指名声风评非容貌)',
3068
+ JIUCHOU),
2973
3069
 
2974
3070
  // 事业
2975
3071
  byStem('禄神', '事业', 'good',
@@ -2994,10 +3090,19 @@ const RULES = [
2994
3090
  WANGSHEN),
2995
3091
  {
2996
3092
  name: '空亡', cat: '凶煞', luck: 'bad',
2997
- rule: '以日柱所在旬的空亡反查各柱地支(日柱旬空)',
3093
+ // 日柱旬空为古法正传;年柱旬空另有一派(以年柱所在旬的空亡查余三支),
3094
+ // 两派所据都是「本旬无支可用」,逻辑同构,故并列而不取此舍彼。
3095
+ rule: '以日柱所在旬的空亡反查各柱地支(日柱旬空,主法);另有年柱旬空一派,一并标出',
2998
3096
  refs: ['day'], refKind: 'branch', viaLabel: '日柱旬空',
2999
3097
  targets: (ctx) => (ctx.kongwang || []).map(T),
3000
3098
  },
3099
+ {
3100
+ name: '年空', cat: '凶煞', luck: 'bad',
3101
+ rule: '以年柱所在旬的空亡反查其余三支(年柱旬空;古法多取日柱旬空,此派并存,命中单独标注)',
3102
+ refs: ['year'], refKind: 'branch', viaLabel: '年柱旬空',
3103
+ slots: ['month', 'day', 'hour'],
3104
+ targets: (ctx) => (ctx.yearKongwang || []).map(T),
3105
+ },
3001
3106
  byPillarGz('十恶大败', '凶煞', 'bad',
3002
3107
  '日柱甲辰、乙巳、丙申、丁亥、戊戌、己丑、庚辰、辛巳、壬申、癸亥(日柱见之)',
3003
3108
  SHIE_DABAI),
@@ -3014,6 +3119,30 @@ const RULES = [
3014
3119
  byStem('流霞', '凶煞', 'bad',
3015
3120
  '甲酉、乙戌、丙未、丁申、戊巳、己午、庚辰、辛卯、壬亥、癸寅(日干查四支;通行表)',
3016
3121
  LIUXIA, ['day']),
3122
+ byMonth('血刃', '凶煞', 'bad',
3123
+ '寅月丑、卯月未、辰月寅、巳月申、午月卯、未月酉、申月辰、酉月戌、戌月巳、亥月亥、子月午、丑月子(月支查四支)',
3124
+ (ctx) => {
3125
+ const ch = XUEYREN[ctx.pillars.month.branch];
3126
+ return ch ? [T(ch)] : [];
3127
+ }),
3128
+ byRefBranch('丧门', '凶煞', 'bad',
3129
+ '年支前两位:子见寅、丑见卯……(年支查余三支)',
3130
+ (ctx) => {
3131
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
3132
+ return [T(BRANCHES[(idx + 2) % 12])];
3133
+ }),
3134
+ byRefBranch('吊客', '凶煞', 'bad',
3135
+ '年支后两位:子见戌、丑见亥……(年支查余三支)',
3136
+ (ctx) => {
3137
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
3138
+ return [T(BRANCHES[(idx + 10) % 12])];
3139
+ }),
3140
+ byRefBranch('披麻', '凶煞', 'bad',
3141
+ '年支后三位:子见酉、丑见戌……(年支查余三支)',
3142
+ (ctx) => {
3143
+ const idx = BRANCHES.indexOf(ctx.pillars.year.branch);
3144
+ return [T(BRANCHES[(idx + 9) % 12])];
3145
+ }),
3017
3146
 
3018
3147
  // 格局
3019
3148
  byMonth('天赦日', '格局', 'good',
@@ -3026,15 +3155,55 @@ const RULES = [
3026
3155
  byPillarGz('金神', '格局', 'neutral',
3027
3156
  '时柱乙丑、己巳、癸酉(时柱见之;严格成格另需日主身旺,此仅记星)',
3028
3157
  JINSHEN, 'hour'),
3158
+ // 拱禄:五日五时,日时同干、虚拱禄支(癸亥日癸丑时拱子、癸丑日癸亥时拱子、丁巳日丁未时拱午、己未日己巳时拱午、戊辰日戊午时拱巳)
3159
+ {
3160
+ name: '拱禄', cat: '格局', luck: 'good',
3161
+ rule: '日时同干而禄位虚拱:癸亥日癸丑时、癸丑日癸亥时(拱子),丁巳日丁未时、己未日己巳时(拱午),戊辰日戊午时(拱巳);只宜虚拱,忌填实空亡',
3162
+ custom: (ctx) => {
3163
+ const GONG = { '癸亥|癸丑': '子', '癸丑|癸亥': '子', '丁巳|丁未': '午', '己未|己巳': '午', '戊辰|戊午': '巳' };
3164
+ const k = `${ctx.pillars.day.ganzhi}|${ctx.pillars.hour.ganzhi}`;
3165
+ return GONG[k] ? [{ pillars: ['day', 'hour'], via: '日时', target: GONG[k] }] : [];
3166
+ },
3167
+ },
3168
+ // 天转 / 地转:以月支定季节查日柱(五行旺极之日,盛极而衰)
3169
+ byMonth('天转', '凶煞', 'bad',
3170
+ '春乙卯、夏丙午、秋辛酉、冬壬子(月支定季节查日柱;「春兔夏马天地转,秋鸡冬鼠便为殃」)',
3171
+ (ctx) => {
3172
+ const season = SEASON[ctx.pillars.month.branch];
3173
+ const gz = ctx.pillars.day.ganzhi;
3174
+ return season && TIANZH[season] === gz ? [GZ(gz)] : [];
3175
+ }, ['day']),
3176
+ byMonth('地转', '凶煞', 'bad',
3177
+ '春辛卯、夏戊午、秋癸酉、冬丙子(月支定季节查日柱)',
3178
+ (ctx) => {
3179
+ const season = SEASON[ctx.pillars.month.branch];
3180
+ const gz = ctx.pillars.day.ganzhi;
3181
+ return season && DIZH[season] === gz ? [GZ(gz)] : [];
3182
+ }, ['day']),
3183
+ // 学堂 / 词馆:年柱纳音五行的长生 / 临官之支(问真采用禄命法第一种;正学堂另需干支同见,此仅记支)
3184
+ {
3185
+ name: '学堂', cat: '文星', luck: 'good',
3186
+ rule: '年纳音五行之长生:金见巳、木见亥、水见申、土见申、火见寅(年纳音查月日时支;学堂为纳音长生,主人聪明文才出众)',
3187
+ refs: ['year'], refKind: 'branch', viaLabel: '年纳音', slots: ['month', 'day', 'hour'],
3188
+ targets: (ctx) => [T(XUETANG[ctx.nayinElement] || '')].filter((t) => t.ch),
3189
+ },
3190
+ {
3191
+ name: '词馆', cat: '文星', luck: 'good',
3192
+ rule: '年纳音五行之临官:金见申、木见寅、水见亥、土见亥、火见巳(年纳音查月日时支;词馆为纳音临官,主文章学识)',
3193
+ refs: ['year'], refKind: 'branch', viaLabel: '年纳音', slots: ['month', 'day', 'hour'],
3194
+ targets: (ctx) => [T(CIGUAN[ctx.nayinElement] || '')].filter((t) => t.ch),
3195
+ },
3029
3196
  {
3030
3197
  name: '天罗', cat: '凶煞', luck: 'bad',
3031
- rule: '四支见戌、亥俱全(戌亥为天罗)',
3032
- custom: (ctx) => chartPattern(ctx, ['戌', '亥']),
3198
+ rule: '戌亥为天罗。法一以年支/日支查余三支:戌见亥、亥见戌即算,不要求四支俱全;'
3199
+ + '法二以年柱纳音查日支:火命见戌或亥。两法任一成立即命中(男忌天罗)',
3200
+ custom: (ctx) => luowangPair(ctx, '戌', '亥', '天罗', ['火']),
3033
3201
  },
3034
3202
  {
3035
3203
  name: '地网', cat: '凶煞', luck: 'bad',
3036
- rule: '四支见辰、巳俱全(辰巳为地网)',
3037
- custom: (ctx) => chartPattern(ctx, ['辰', '巳']),
3204
+ rule: '辰巳为地网。法一以年支/日支查余三支:辰见巳、巳见辰即算,不要求四支俱全;'
3205
+ + '法二以年柱纳音查日支:水命、土命见辰或巳。两法任一成立即命中(女忌地网)',
3206
+ custom: (ctx) => luowangPair(ctx, '辰', '巳', '地网', ['水', '土']),
3038
3207
  },
3039
3208
  {
3040
3209
  name: '三奇贵人', cat: '格局', luck: 'good',
@@ -3050,11 +3219,29 @@ const RULES = [
3050
3219
  },
3051
3220
  ];
3052
3221
 
3053
- /** 四支俱全即命中(天罗地网) */
3054
- function chartPattern(ctx, branches) {
3055
- const keys = PILLAR_KEYS.filter((k) => branches.includes(ctx.pillars[k].branch));
3056
- if (keys.length < branches.length) return [];
3057
- return [{ pillars: keys, via: '四支', target: branches.join('') }];
3222
+ /**
3223
+ * 天罗地网:两法并行,任一成立即命中。
3224
+ * 法一(主):以年支/日支查余三支——戌见亥、亥见戌为天罗;辰见巳、巳见辰为地网。只需两支成对,
3225
+ * 不要求四支俱全(旧实现要求「戌亥俱全」,四支里必须同时出现戌与亥,漏掉大半)。
3226
+ * 法二(辅):以年柱纳音查日支——火命见戌或亥为天罗,水命、土命见辰或巳为地网,金木二命无。
3227
+ * 命中时把「起查柱」与「应柱」都标出来,否则只看天罗在亥那一柱,读盘时找不到起因。
3228
+ */
3229
+ function luowangPair(ctx, a, b, label, nayinEls) {
3230
+ const out = [];
3231
+ // 法一:以年/日支查余三支(只需两支成对)
3232
+ for (const ref of ['year', 'day']) {
3233
+ const rb = ctx.pillars[ref].branch;
3234
+ const want = rb === a ? b : rb === b ? a : null;
3235
+ if (!want) continue;
3236
+ const hits = PILLAR_KEYS.filter((k) => k !== ref && ctx.pillars[k].branch === want);
3237
+ for (const k of hits) out.push({ pillars: [ref, k], via: BRANCH_VIA[ref], target: want });
3238
+ }
3239
+ // 法二:以年纳音查日支
3240
+ if (nayinEls.includes(ctx.nayinElement) && (ctx.pillars.day.branch === a || ctx.pillars.day.branch === b)) {
3241
+ out.push({ pillars: ['day'], via: `年纳音${ctx.nayinElement}`, target: ctx.pillars.day.branch });
3242
+ }
3243
+ void label;
3244
+ return out;
3058
3245
  }
3059
3246
 
3060
3247
  // ── 执行 ────────────────────────────────────────────────────────
@@ -3086,12 +3273,19 @@ function runRule(r, ctx) {
3086
3273
  * 计算八字神煞
3087
3274
  * @param {object} pillars { year, month, day, hour },每柱需有 stem/branch/ganzhi
3088
3275
  * @param {string[]} kongwang 日柱旬空的两个地支
3276
+ * @param {object} [opts] { gender?: 'male'|'female', yearNayinElement?: string }
3277
+ * gender 供勾绞煞 / 元辰辨阴阳男女;yearNayinElement 供学堂 / 词馆(年柱纳音五行)
3089
3278
  * @returns {{ detail: object, groups: array, names: string[], count: number }}
3090
3279
  * detail[key] = [{ name, cat, luck, via, target, rule }]
3091
3280
  * groups = [{ cat, items: [{ name, luck, pillars, via, target, rule }] }]
3092
3281
  */
3093
- function computeBaziShensha(pillars, kongwang = []) {
3094
- const ctx = { pillars, kongwang };
3282
+ function computeBaziShensha(pillars, kongwang = [], opts = {}) {
3283
+ const ctx = {
3284
+ pillars, kongwang,
3285
+ gender: opts.gender,
3286
+ nayinElement: opts.yearNayinElement,
3287
+ yearKongwang: opts.yearKongwang || [],
3288
+ };
3095
3289
  const items = [];
3096
3290
  for (const r of RULES) {
3097
3291
  const hits = runRule(r, ctx);
@@ -6671,7 +6865,15 @@ function buildBazi(input) {
6671
6865
  const monthBranch = pillars.month.branch;
6672
6866
 
6673
6867
  // 神煞:表驱动、双查法(年干/日干、年支/日支、月支、日柱、时柱),详见 engine/shensha.js
6674
- const shensha = computeBaziShensha(pillars, kongwang);
6868
+ // gender 供勾绞煞/元辰辨阴阳男女;年柱纳音五行供学堂/词馆
6869
+ const shensha = computeBaziShensha(pillars, kongwang, {
6870
+ gender,
6871
+ // nayin() 返回的是纳音名(字符串),不是对象——取五行须走 nayinOf()。
6872
+ // 这里长期写成 nayin(yearIdx).element 恒为 undefined,导致学堂/词馆(纳音长生/临官)
6873
+ // 与童子煞纳音法在真实排盘里从未命中过;单测因为直接喂了 yearNayinElement 而没暴露。
6874
+ yearNayinElement: nayinOf(yearIdx).element,
6875
+ yearKongwang: xunKongOf(pillars.year.ganzhi),
6876
+ });
6675
6877
 
6676
6878
  for (const key of ['year', 'month', 'day', 'hour']) {
6677
6879
  const pl = pillars[key];
@@ -9476,7 +9678,7 @@ return { PALACE_FIELD, STAR_EXTRA, STAR_PAIR, SHA_STAR, MAIN_SHA, FU_STAR, HUA_R
9476
9678
 
9477
9679
  __mods.ziwei = (function () {
9478
9680
  // 紫微斗数安星:命身宫 → 五行局 → 紫微/天府 → 十四主星 → 诸辅煞杂曜 → 十二宫 → 四化 → 大限小限流年 → 格局
9479
- const { STEMS, BRANCHES, ganzhiName, sexagenaryIndex, jdToParts, partsToJd, formatJd } = (__mods.calendar || {});
9681
+ const { STEMS, BRANCHES, ganzhiName, sexagenaryIndex, jdToParts, partsToJd, formatJd, dayGanzhiIndex } = (__mods.calendar || {});
9480
9682
  const { solarToLunar } = (__mods.lunar || {});
9481
9683
  const { applyTrueSolar } = (__mods.bazi || {});
9482
9684
  const { applyDst } = (__mods.dst || {});
@@ -9576,6 +9778,13 @@ const ZIWEI_SCHOOL_DEFAULTS = Object.freeze({
9576
9778
  zuoyaoSchool: 'minimal',
9577
9779
  liunianSihua: 'year-stem',
9578
9780
  sihuaVariant: 'default',
9781
+ // 中州派成组差异:off 全部用通行本(默认,默认盘逐字节不变);zhongzhou 表示
9782
+ // 「跟随 tianshiTianshang 的 zhongzhou 档」—— 那把开关切到中州派时,这一组跟着生效;
9783
+ // 单独选 off/zhongzhou 而不动 tianshiTianshang 也可。
9784
+ // jieluSchool 截路空亡:zhongzhou 不排(截空、副截、旬空、副旬整组不出)
9785
+ // suipoSchool 岁前十二神:zhongzhou 把「大耗」改记作「岁破」(宫位不变,只换名字)
9786
+ jieluSchool: 'off',
9787
+ suipoSchool: 'off',
9579
9788
  });
9580
9789
 
9581
9790
  const ZIWEI_SCHOOL_OPTIONS = Object.freeze({
@@ -9601,6 +9810,11 @@ const ZIWEI_SCHOOL_OPTIONS = Object.freeze({
9601
9810
  // 只列与 default 不同的那一档(default 之外全是「换掉某一干」的显式选择)。
9602
9811
  sihuaVariant: ['default', '廉破曲阳', '贪阴阳机', '阳武同阴', '阳武府同', '阳武府相',
9603
9812
  '阳武同相', '巨阳武昌', '梁紫府武', '梁紫相武', '破巨阳贪'],
9813
+ // 中州派成组差异(两档各自独立,可单独开):
9814
+ // jieluSchool off 截路空亡照常 / zhongzhou 整组不排(截空、副截、旬空、副旬都不出)
9815
+ // suipoSchool off 岁前十二神照常 / zhongzhou 把「大耗」改记作「岁破」
9816
+ jieluSchool: ['off', 'zhongzhou'],
9817
+ suipoSchool: ['off', 'zhongzhou'],
9604
9818
  });
9605
9819
 
9606
9820
  function ziweiSchoolsOf(input = {}) {
@@ -10063,7 +10277,11 @@ function buildZiwei(input) {
10063
10277
  if (schools.tiankongSchool !== 'off') {
10064
10278
  put(schools.tiankongSchool === 'shun-jia-shi' ? yb + hb : yb + 1, '天空', '杂曜');
10065
10279
  }
10066
- if (schools.jiekongSchool !== 'off') {
10280
+ // 截路空亡:zhongzhou(中州派)整组不排——截空、副截、旬空、副旬都不出。
10281
+ // 依据《中州派紫微斗数》:截路空亡不入中州派体系;通行本才排。
10282
+ // 这一组只影响杂曜清单,不动宫盘、主星、十二宫位置。
10283
+ const skipJielu = schools.jieluSchool === 'zhongzhou';
10284
+ if (schools.jiekongSchool !== 'off' && !skipJielu) {
10067
10285
  const jk = JIEKONG[schools.jiekongSchool][yearStem];
10068
10286
  if (Array.isArray(jk)) {
10069
10287
  put(jk[0], '截空', '杂曜');
@@ -10130,13 +10348,17 @@ function buildZiwei(input) {
10130
10348
  const csStart = schools.changshengSchool === 'huo-tu' && bureau.number === 5
10131
10349
  ? 2
10132
10350
  : CHANGSHENG_START[bureau.number];
10351
+ // 岁前十二神:中州派把「大耗」改记作「岁破」(宫位不变,只换名字)。
10352
+ const renameDahao = schools.suipoSchool === 'zhongzhou';
10133
10353
  const shenshaAt = (branch) => {
10134
10354
  const d = forward ? 1 : -1;
10355
+ let suiqi = SUIQI_NAMES[fix(branch - yb)];
10356
+ if (renameDahao && suiqi === '大耗') suiqi = '岁破';
10135
10357
  return {
10136
10358
  changsheng: CHANGSHENG_NAMES[fix(d * (branch - csStart))],
10137
10359
  boshi: BOSHI_NAMES[fix(d * (branch - lucun))],
10138
10360
  jiangqian: JIANGQIAN_NAMES[fix(branch - JIANGXING[triad])],
10139
- suiqi: SUIQI_NAMES[fix(branch - yb)],
10361
+ suiqi,
10140
10362
  };
10141
10363
  };
10142
10364
 
@@ -10173,6 +10395,10 @@ function buildZiwei(input) {
10173
10395
  });
10174
10396
  }
10175
10397
 
10398
+ // 来因宫(iztro 口径):生年天干与宫干相同的宫,子丑两宫除外(这两支五虎遁永不排甲己…的生年干)。
10399
+ // 之所以叫「来因」:此宫是今生命局展开的起点,主「因何而来」,看一个人的先天因缘与执念方向。
10400
+ const laiyin = palaces.find((p) => p.stem === yearStem && p.branch !== '子' && p.branch !== '丑') || null;
10401
+
10176
10402
  // 昌曲专论:与丑未的关系、是否入命三方、吸收面的清杂
10177
10403
  const changqu = (() => {
10178
10404
  const set = new Set([wenchang, wenqu]);
@@ -10252,7 +10478,20 @@ function buildZiwei(input) {
10252
10478
  liutuo: BRANCHES[fix(lu - 1)],
10253
10479
  liukui: BRANCHES[kuiyueTable[gz.stem][0]],
10254
10480
  liuyue: BRANCHES[kuiyueTable[gz.stem][1]],
10481
+ // 流曜补全(流昌曲按流年干、流马按流年支三合、流鸾流喜按流年支、年解按流年支)
10482
+ ...(() => {
10483
+ const s = horoscopeStarsByBranch(gz, true);
10484
+ const out = {};
10485
+ for (const [name, br] of Object.entries(s)) {
10486
+ if (name === '流禄' || name === '流羊' || name === '流陀' || name === '流魁' || name === '流钺') continue;
10487
+ out[name] = br;
10488
+ }
10489
+ return out;
10490
+ })(),
10255
10491
  mutagen: Object.entries(sihuaTableOf(lnStemOf, schools)).map(([s, k]) => `${s}化${k}`),
10492
+ // 流年十二神:岁前/将前随流年支重安(iztro getYearly12 口径)
10493
+ lnSuiqi: suiqiByBranch(gz.branch, schools.suipoSchool),
10494
+ lnJiangqian: jiangqianByBranch(gz.branch),
10256
10495
  daxian: dxPalace
10257
10496
  ? { palace: dxPalace.palace, start: dxPalace.daxian.start, end: dxPalace.daxian.end }
10258
10497
  : null,
@@ -10297,6 +10536,30 @@ function buildZiwei(input) {
10297
10536
  },
10298
10537
  hourBranch,
10299
10538
  yearGanzhi: yearGanzhi.name,
10539
+ ...((() => {
10540
+ // 非节气四柱(紫微斗数口径,文墨「显示非节气四柱」开关的数据源):
10541
+ // 紫微按农历排盘,年柱以正月初一换岁(lunarYear 已是此口径)、月柱按农历月配五虎遁;
10542
+ // 日柱、时柱与节气版完全一致(日时不受节气影响)。与八字(节气)四柱只在立春前后的
10543
+ // 出生者上年/月两柱有差异,正好给用户对照。
10544
+ const yIdx = (((lunarRaw.lunarYear - 4) % 60) + 60) % 60;
10545
+ const nYinStem = ((STEMS.indexOf(yearGanzhi.stem) % 5) * 2 + 2) % 10;
10546
+ const mBranchIdx = 2 + (lunarMonth - 1) >= 12 ? 2 + (lunarMonth - 1) - 12 : 2 + (lunarMonth - 1); // 正月建寅
10547
+ const mStemIdx = (nYinStem + lunarMonth - 1) % 10;
10548
+ const nJd = partsToJd({ year, month, day, hour: 12 });
10549
+ const dIdx = dayGanzhiIndex(Math.floor(nJd + 0.5));
10550
+ const dStemIdx = dIdx % 10;
10551
+ const ziStem = ((dStemIdx % 5) * 2) % 10;
10552
+ const hIdx = sexagenaryIndex((ziStem + hb) % 10, hb);
10553
+ return {
10554
+ nonJieqi: {
10555
+ year: ganzhiName(yIdx).name,
10556
+ month: ganzhiName(sexagenaryIndex(mStemIdx, mBranchIdx)).name,
10557
+ day: ganzhiName(dIdx).name,
10558
+ hour: ganzhiName(hIdx).name,
10559
+ note: '非节气(农历)口径:年以正月初一、月以朔为界;与八字节气四柱仅立春前后出生者有差',
10560
+ },
10561
+ };
10562
+ })()),
10300
10563
  soul: {
10301
10564
  palace: '命宫',
10302
10565
  branch: BRANCHES[soulIndex],
@@ -10334,6 +10597,8 @@ function buildZiwei(input) {
10334
10597
  ziwei: BRANCHES[ziwei],
10335
10598
  tianfu: BRANCHES[tianfu],
10336
10599
  changqu,
10600
+ // 来因宫:{branch, palace, ganzhi} 或 null(理论上不 null,防御性保留)
10601
+ laiyin: laiyin ? { branch: laiyin.branch, palace: laiyin.palace, ganzhi: laiyin.ganzhi } : null,
10337
10602
  // 杂曜全表的这一层只写「这一盘怎么定宫」:面板、独立页与「复制提问」共用。
10338
10603
  // 默认(minimal)时 placed 为空,但 skipped 照旧给出,说明为什么少了两颗。
10339
10604
  zuoyao: {
@@ -10365,7 +10630,7 @@ function buildZiwei(input) {
10365
10630
  rules: {
10366
10631
  ziweiPlacement: '借数法:局数除日数取商,商数自寅宫顺行,借数奇逆偶顺',
10367
10632
  mutagen: '生年四化用中州派常用表;庚干可按流派切换(默认阳武阴同,另有同科阴忌、府科、府科相忌、同科相忌)',
10368
- schools: `流派开关:亮度 ${schools.brightnessSchool}、庚干四化 ${schools.gengYearSihua}、天马 ${schools.tianmaSchool}、天空 ${schools.tiankongSchool}、截空 ${schools.jiekongSchool}、魁钺 ${schools.kuiyueSchool}、天伤天使 ${schools.tianshiTianshang}、长生 ${schools.changshengSchool}、晚子时 ${schools.lateZishi}、身主 ${schools.shenzhuSchool}(common 子午皆火星 / verse 子铃午火)、命主 ${schools.mingzhuSchool}(soul-branch 按命宫地支 / year-branch 按生年年支)、杂曜 ${schools.zuoyaoSchool}(minimal 49 颗 / full 61 颗)、流年四化取干 ${schools.liunianSihua}(year-stem 按流年天干 / life-palace-stem 按流年命宫宫干)、十干四化 ${schools.sihuaVariant}(default 各干通行表,其余为文墨变体整表替换);庚干四化只作用于生年四化,sihuaVariant 换的是宫干自化、大限宫干、飞星与流年共用的那张表`,
10633
+ schools: `流派开关:亮度 ${schools.brightnessSchool}、庚干四化 ${schools.gengYearSihua}、天马 ${schools.tianmaSchool}、天空 ${schools.tiankongSchool}、截空 ${schools.jiekongSchool}、魁钺 ${schools.kuiyueSchool}、天伤天使 ${schools.tianshiTianshang}、长生 ${schools.changshengSchool}、晚子时 ${schools.lateZishi}、身主 ${schools.shenzhuSchool}(common 子午皆火星 / verse 子铃午火)、命主 ${schools.mingzhuSchool}(soul-branch 按命宫地支 / year-branch 按生年年支)、杂曜 ${schools.zuoyaoSchool}(minimal 49 颗 / full 61 颗)、流年四化取干 ${schools.liunianSihua}(year-stem 按流年天干 / life-palace-stem 按流年命宫宫干)、十干四化 ${schools.sihuaVariant}(default 各干通行表,其余为文墨变体整表替换)、截路空亡 ${schools.jieluSchool}(off 照常排 / zhongzhou 整组不排)、岁前十二神 ${schools.suipoSchool}(off 照常 / zhongzhou 大耗改岁破);庚干四化只作用于生年四化,sihuaVariant 换的是宫干自化、大限宫干、飞星与流年共用的那张表`,
10369
10634
  zuoyao: schools.zuoyaoSchool === 'full'
10370
10635
  ? `全表十二颗已安:生月 解神、天巫、天月、阴煞;生年干 天官、天福、天厨;生年支 蜚廉、天德、年解;命宫+生年支 天才、身宫+生年支 天寿。蜚廉与博士十二神的飞廉同名异星,年解与月解(解神)分列,天德与岁前十二神的天德同名异宫。`
10371
10636
  : `本盘按 49 颗的通行简盘安星,未排全表杂曜。开 zuoyaoSchool: "full" 追加十二颗:解神、天巫、天月、阴煞(生月)、天官、天福、天厨(生年干)、蜚廉、天德、年解(生年支)、天才(命宫+生年支)、天寿(身宫+生年支),每颗都带安星诀。`,
@@ -10650,6 +10915,34 @@ const PATTERN_TONE = {
10650
10915
  刑囚夹印: 'bad', 泛水桃花: 'bad',
10651
10916
  };
10652
10917
 
10918
+ // ── 限流十二神(iztro 口径):随流年干支重安的只有岁前/将前两组 ──
10919
+ // 长生十二神、博士十二神只在本命排盘算一次,horoscope scope 不重算(iztro
10920
+ // FunctionalAstrolabe._getHoroscopeBySolarDate 里 yearlyDecStar=getYearly12(solarDate)
10921
+ // 只重出 suiqian12/jiangqian12,长生与博士留在本命宫位上)。
10922
+ // 岁前:流年支起岁建,顺数 SUIQI_NAMES;将前:寅午戌起午、申子辰起子、巳酉丑起酉、亥卯未起卯。
10923
+ function suiqiByBranch(branch, suipoSchool) {
10924
+ const b = BRANCHES.indexOf(branch);
10925
+ if (b < 0) return {};
10926
+ const out = {};
10927
+ for (let i = 0; i < 12; i++) {
10928
+ let name = SUIQI_NAMES[i];
10929
+ if (suipoSchool === 'zhongzhou' && name === '大耗') name = '岁破';
10930
+ out[name] = BRANCHES[fix(b + i)];
10931
+ }
10932
+ return out;
10933
+ }
10934
+
10935
+ function jiangqianByBranch(branch) {
10936
+ const b = BRANCHES.indexOf(branch);
10937
+ if (b < 0) return {};
10938
+ const triad = ['寅午戌', '申子辰', '巳酉丑', '亥卯未'].find((t) => t.includes(branch));
10939
+ if (!triad) return {};
10940
+ const start = JIANGXING[triad];
10941
+ const out = {};
10942
+ for (let i = 0; i < 12; i++) out[JIANGQIAN_NAMES[i]] = BRANCHES[fix(start + i)];
10943
+ return out;
10944
+ }
10945
+
10653
10946
  // ── 大限一览:宫干四化落宫、三方主星 ──────────────────────────
10654
10947
 
10655
10948
  function daxianList(chart) {
@@ -10672,6 +10965,11 @@ function daxianList(chart) {
10672
10965
  selfHua: p.selfHua,
10673
10966
  sanfang: p.sanfang,
10674
10967
  mainStars: mainOf(p.sanfang),
10968
+ // 运曜:按大限宫干支安 10 颗(魁钺昌曲禄羊陀马鸾喜),映射 星名→地支
10969
+ yunStars: horoscopeStarsByBranch(p.ganzhi, false),
10970
+ // 限流十二神:随大限宫支重安的岁前/将前两组(长生、博士不随限流变)
10971
+ yunSuiqi: suiqiByBranch(p.branch, chart.schools.suipoSchool),
10972
+ yunJiangqian: jiangqianByBranch(p.branch),
10675
10973
  mutagenHits: Object.entries(sihuaTableOf(p.stem, chart.schools)).map(([star, type]) => {
10676
10974
  const b = starBranchOf(chart, star);
10677
10975
  return { star, type, branch: b >= 0 ? BRANCHES[b] : null, palace: b >= 0 ? chart.palaces.find((x) => x.branch === BRANCHES[b])?.palace ?? null : null };
@@ -10854,7 +11152,61 @@ function flyComposite(chart, { from = '命宫' } = {}) {
10854
11152
  };
10855
11153
  }
10856
11154
 
10857
- // ── 流年四化落宫 ────────────────────────────────────────────
11155
+ // ── 限流星曜(运曜/流曜)────────────────────────────────────
11156
+ // 口径依据 iztro star/horoscopeStar.ts + star/location.ts(本命同表换干支重算):
11157
+ // · 魁钺、昌曲、禄羊陀 按运限天干;天马按运限地支三合;红鸾天喜按运限地支(卯起逆数)。
11158
+ // · 流年另加「年解」:戌上起子,逆数至流年地支。
11159
+ // 与本命安星的差异只有两点:昌曲在本命按时支(戌起逆数/辰起顺数),限流按天干
11160
+ //(甲昌巳曲酉、乙昌午曲申、丙戊昌申曲午、丁己昌酉曲巳、庚昌亥曲卯、辛昌子曲寅、壬昌寅曲辰、癸昌卯曲巳);
11161
+ // 火铃不随限流重安(iztro 同样只安这 10+1 颗)。
11162
+ const LIU_CHANGQU = {
11163
+ 甲: [5, 9], 乙: [6, 8], 丙: [8, 6], 戊: [8, 6], 丁: [9, 5], 己: [9, 5],
11164
+ 庚: [11, 3], 辛: [0, 2], 壬: [2, 4], 癸: [3, 5],
11165
+ };
11166
+
11167
+ const NIANJIE_OF = { 子: 10, 丑: 9, 寅: 8, 卯: 7, 辰: 6, 巳: 5, 午: 4, 未: 3, 申: 2, 酉: 1, 戌: 0, 亥: 11 };
11168
+
11169
+ /** 前缀:大限叫运X、流年叫流X(iztro 同款命名) */
11170
+ const HS_PREFIX = (yearly) => (yearly ? '流' : '运');
11171
+
11172
+ /** 按一组干支安限流星曜,返回 12 宫(按地支序)各宫的星名数组。yearly=true 时前缀用「流」并加年解。 */
11173
+ function horoscopeStarsOf(gz, yearly = false) {
11174
+ const stem = gz.stem ?? String(gz).replace(/[子丑寅卯辰巳午未申酉戌亥]$/, '');
11175
+ const branch = gz.branch ?? String(gz).slice(-1);
11176
+ const sb = BRANCHES.indexOf(branch);
11177
+ const pre = HS_PREFIX(yearly);
11178
+ const lu = LUCUN[stem];
11179
+ const [kui, yue] = KUI_YUE[stem];
11180
+ const [chang, qu] = LIU_CHANGQU[stem];
11181
+ const ma = TIANMA[triadOf(sb)];
11182
+ const luang = fix(3 - sb);
11183
+ const byBranch = Array.from({ length: 12 }, () => []);
11184
+ const add = (idx, name) => byBranch[fix(idx)].push(name);
11185
+ add(kui, `${pre}魁`);
11186
+ add(yue, `${pre}钺`);
11187
+ add(chang, `${pre}昌`);
11188
+ add(qu, `${pre}曲`);
11189
+ add(lu, `${pre}禄`);
11190
+ add(lu + 1, `${pre}羊`);
11191
+ add(lu - 1, `${pre}陀`);
11192
+ add(ma, `${pre}马`);
11193
+ add(luang, `${pre}鸾`);
11194
+ add(luang + 6, `${pre}喜`);
11195
+ if (yearly) add(NIANJIE_OF[branch], '年解');
11196
+ return byBranch;
11197
+ }
11198
+
11199
+ /** 把 12 宫的星名数组转成 {星名: 地支}(供 daxianList/liunian 挂字段) */
11200
+ function horoscopeStarsByBranch(gz, yearly = false) {
11201
+ const byBranch = horoscopeStarsOf(gz, yearly);
11202
+ const out = {};
11203
+ byBranch.forEach((stars, i) => {
11204
+ for (const name of stars) out[name] = BRANCHES[i];
11205
+ });
11206
+ return out;
11207
+ }
11208
+
11209
+
10858
11210
 
10859
11211
  function enrichLiunian(chart) {
10860
11212
  for (const ln of chart.liunian) {
@@ -10964,7 +11316,7 @@ function flowOf(chart, asOf) {
10964
11316
  };
10965
11317
  }
10966
11318
 
10967
- return { PALACE_NAMES, BRIGHTNESS, MUTAGEN, ZIWEI_SCHOOL_DEFAULTS, ZIWEI_SCHOOL_OPTIONS, ziweiSchoolsOf, BRIGHTNESS_SCHOOLS, schoolBrightness, SIHUA_VARIANTS, sihuaTableOf, natalSihuaTableOf, KUI_YUE_SCHOOLS, LUCUN, KUI_YUE, MING_ZHU, SHEN_ZHU, triadOf, HUO_START, LING_START, TIANMA, XIANCHI, HUAGAI, JIANGXING, GUCHEN, GUASU, posuIndex, XIAOXIAN_START, CHANGSHENG_NAMES, BOSHI_NAMES, JIANGQIAN_NAMES, SUIQI_NAMES, CHANGSHENG_START, hourBranchIndex, ziweiTianfuIndex, xiaoxianAge, LEAP_POLICIES, leapPolicyOf, effectiveLunarMonth, buildZiwei, detectPatterns, PATTERN_BREAK_RULES, PATTERN_TONE, daxianList, FLY_TYPES, flyStarOf, flyStars, flyChain, flyChains, flyComposite, enrichLiunian, asOfParts, flowOf };
11319
+ return { PALACE_NAMES, BRIGHTNESS, MUTAGEN, ZIWEI_SCHOOL_DEFAULTS, ZIWEI_SCHOOL_OPTIONS, ziweiSchoolsOf, BRIGHTNESS_SCHOOLS, schoolBrightness, SIHUA_VARIANTS, sihuaTableOf, natalSihuaTableOf, KUI_YUE_SCHOOLS, LUCUN, KUI_YUE, MING_ZHU, SHEN_ZHU, triadOf, HUO_START, LING_START, TIANMA, XIANCHI, HUAGAI, JIANGXING, GUCHEN, GUASU, posuIndex, XIAOXIAN_START, CHANGSHENG_NAMES, BOSHI_NAMES, JIANGQIAN_NAMES, SUIQI_NAMES, CHANGSHENG_START, hourBranchIndex, ziweiTianfuIndex, xiaoxianAge, LEAP_POLICIES, leapPolicyOf, effectiveLunarMonth, buildZiwei, detectPatterns, PATTERN_BREAK_RULES, PATTERN_TONE, suiqiByBranch, jiangqianByBranch, daxianList, FLY_TYPES, flyStarOf, flyStars, flyChain, flyChains, flyComposite, horoscopeStarsOf, horoscopeStarsByBranch, enrichLiunian, asOfParts, flowOf };
10968
11320
  })();
10969
11321
 
10970
11322
  __mods['tarot-cards'] = (function () {
@@ -27274,6 +27626,7 @@ const ZIWEI_SCHOOL_KEYS = [
27274
27626
  'brightnessSchool', 'gengYearSihua', 'tianmaSchool', 'tiankongSchool', 'jiekongSchool',
27275
27627
  'kuiyueSchool', 'tianshiTianshang', 'changshengSchool', 'lateZishi', 'shenzhuSchool',
27276
27628
  'mingzhuSchool', 'zuoyaoSchool', 'liunianSihua', 'sihuaVariant',
27629
+ 'jieluSchool', 'suipoSchool',
27277
27630
  ];
27278
27631
 
27279
27632
  /** 紫微流派参数原样透传;非法值交给引擎按默认口径回退 */
@@ -27408,6 +27761,7 @@ function ziweiText(r) {
27408
27761
  jiekongSchool: '截空', kuiyueSchool: '魁钺', tianshiTianshang: '天伤天使',
27409
27762
  changshengSchool: '长生', lateZishi: '晚子时', shenzhuSchool: '身主', mingzhuSchool: '命主取法',
27410
27763
  zuoyaoSchool: '杂曜全表', liunianSihua: '流年四化取干', sihuaVariant: '十干四化变体',
27764
+ jieluSchool: '截路空亡', suipoSchool: '岁前十二神',
27411
27765
  };
27412
27766
  const diff = Object.entries(labels)
27413
27767
  .filter(([key]) => r.schools[key] && r.schools[key] !== ZIWEI_SCHOOL_DEFAULTS[key])
@@ -29309,7 +29663,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, parseDate
29309
29663
  })();
29310
29664
 
29311
29665
  window.Engine = __mods.index;
29312
- window.__MP_VER = "0.64.0";
29666
+ window.__MP_VER = "0.66.0";
29313
29667
  </script>
29314
29668
  <script>
29315
29669
  // 两个 UI 共用的表单规则:宿主面板(src/app.js)与独立页(src/web-ui.js)。
@@ -29518,6 +29872,9 @@ window.__MP_VER = "0.64.0";
29518
29872
  .pp-body 是 static 定位的整块视图容器,::after 却是 absolute,
29519
29873
  结果每次排盘都会在文档左上角飘出一个孤立的灰色「身」方块。已删除。 */
29520
29874
  .pp-shen::after{content:"身";position:absolute;top:4px;left:4px;font-size:12px;color:var(--on-accent);background:var(--muted);padding:0 3px;border-radius:2px;z-index:1}
29875
+ /* 来因宫角标:右上角「因」,与身宫左上角对称;开关 ppm-d-laiyin 控显隐(默认勾) */
29876
+ .pp-laiyin::after{content:"因";position:absolute;top:4px;right:4px;font-size:12px;color:var(--on-accent);background:var(--ji);padding:0 3px;border-radius:2px;z-index:1}
29877
+ #ppm-d-laiyin:not(:checked)~.pp-body .pp-laiyin::after{display:none}
29521
29878
  .pp-legend{display:flex;gap:10px;align-items:center;justify-content:center;font-size:12px;color:var(--muted);margin-top:6px}
29522
29879
  .pp-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;font-family:system-ui,sans-serif}
29523
29880
  .pp-chip{font-size:12.5px;padding:3px 9px;border:1px solid var(--line);color:var(--muted);background:var(--bg)}
@@ -29600,6 +29957,18 @@ window.__MP_VER = "0.64.0";
29600
29957
  .pp-fx i{font-style:normal;white-space:nowrap;margin-right:4px}
29601
29958
  .pp-fx i em{font-style:normal;color:var(--faint)}
29602
29959
  #ppm-sc-feixing:checked~.pp-body .pp-fx{display:block}
29960
+ /* 运曜/流曜行:默认隐藏,勾选「显示运曜和流曜」才显示(限流盘显示组,纯增强) */
29961
+ .pp-hs{display:none;margin-top:3px;font-size:10.5px;line-height:1.55;color:var(--muted);border-top:1px dashed var(--hair);padding-top:2px}
29962
+ .pp-hs i{font-style:normal;white-space:nowrap;margin-right:4px}
29963
+ .pp-hs i em{font-style:normal;color:var(--faint)}
29964
+ #ppm-d-hs:checked~.pp-body .pp-hs{display:block}
29965
+ /* 日干四化:宫格下方一行小字(禄权科忌四颗化星落宫),默认隐藏,勾 ppm-d-rigan 才显示 */
29966
+ .pp-rigan{display:none;margin-top:6px;padding:5px 11px;border:1px solid var(--line);background:var(--band);font-size:12px;line-height:1.7;color:var(--muted)}
29967
+ .pp-rigan b{color:var(--ink);font-weight:600;margin-right:8px}
29968
+ .pp-rigan i{font-style:normal;margin-right:10px;white-space:nowrap}
29969
+ .pp-rigan i em{font-style:normal}
29970
+ .pp-rigan i .rl{color:var(--lu)}.pp-rigan i .rq{color:var(--quan)}.pp-rigan i .rk{color:var(--ke)}.pp-rigan i .rj{color:var(--ji)}
29971
+ #ppm-d-rigan:checked~.pp-rigan{display:block}
29603
29972
  #ppm-sc-feixing:checked~.pp-body .pp-m.view,
29604
29973
  #ppm-sc-feixing:checked~.pp-body .pp-viewhua{display:none}
29605
29974
  /* 三合派:四化信息整体退场(生年/自化/大限流年/图例/飞星链 chips/自化箭头) */
@@ -30848,12 +31217,71 @@ window.__MP_VER = "0.64.0";
30848
31217
  .pp table tbody tr:hover{background:var(--hover)}
30849
31218
  /* 结果卡阴影分层:默认最轻,hover 时浮起一档(只对可交互的卡,避免整页抖动) */
30850
31219
  .pp{transition:box-shadow var(--dur) var(--ease)}
31220
+
31221
+ /* —— 文墨式「显示设置」14 项(学文墨天机 group_fxp / group_4hb / group_3hp)——
31222
+ 纯 CSS 显隐:勾了盘面立刻变,不重排盘、不进引擎、不进工具 schema。
31223
+ 复选框必须是 .pp 的直接子元素,才能用 ~ 命中后面的 .pp-body / .pp-zhlegend。
31224
+ 组1/组2 默认勾(:not(:checked) 才隐藏),组3 默认不勾(:checked 才增强,默认盘观感不变)。 */
31225
+ .pp-disp{font-family:var(--sans);border-bottom:1px solid var(--line);background:var(--band);font-size:12px}
31226
+ .pp-disp>summary{cursor:pointer;padding:5px 12px;color:var(--muted);user-select:none;list-style:none;min-height:32px;display:flex;align-items:center}
31227
+ .pp-disp>summary::-webkit-details-marker{display:none}
31228
+ .pp-disp>summary::before{content:"▸";margin-right:6px;font-size:10px;color:var(--label)}
31229
+ .pp-disp[open]>summary::before{content:"▾"}
31230
+ .pp-disp-g{display:flex;flex-wrap:wrap;gap:2px 14px;padding:3px 12px 8px;border-top:1px dashed var(--hair)}
31231
+ .pp-disp-g>span{width:100%;font-size:11px;color:var(--label);letter-spacing:.06em;margin-bottom:1px}
31232
+ .pp-disp label{display:inline-flex;align-items:center;gap:5px;cursor:pointer;color:var(--ink2);user-select:none;padding:2px 0;min-height:26px}
31233
+ .pp-disp-box{width:13px;height:13px;border:1px solid var(--line);border-radius:3px;background:var(--card);display:inline-block;flex:none}
31234
+ .pp-disp label:hover .pp-disp-box{border-color:var(--lu)}
31235
+ /* 勾选态:12 条 :checked 各自点亮自己 label 的方框(复选框与 .pp-disp 同为 .pp 直接子元素) */
31236
+ #ppm-d-ss:checked~.pp-disp label[for="ppm-d-ss"] .pp-disp-box,
31237
+ #ppm-d-sbg:checked~.pp-disp label[for="ppm-d-sbg"] .pp-disp-box,
31238
+ #ppm-d-zha:checked~.pp-disp label[for="ppm-d-zha"] .pp-disp-box,
31239
+ #ppm-d-ages:checked~.pp-disp label[for="ppm-d-ages"] .pp-disp-box,
31240
+ #ppm-d-sizhu:checked~.pp-disp label[for="ppm-d-sizhu"] .pp-disp-box,
31241
+ #ppm-d-small:checked~.pp-disp label[for="ppm-d-small"] .pp-disp-box,
31242
+ #ppm-d-shen:checked~.pp-disp label[for="ppm-d-shen"] .pp-disp-box,
31243
+ #ppm-d-hua:checked~.pp-disp label[for="ppm-d-hua"] .pp-disp-box,
31244
+ #ppm-d-sf:checked~.pp-disp label[for="ppm-d-sf"] .pp-disp-box,
31245
+ #ppm-d-laiyin:checked~.pp-disp label[for="ppm-d-laiyin"] .pp-disp-box,
31246
+ #ppm-d-zha-bold:checked~.pp-disp label[for="ppm-d-zha-bold"] .pp-disp-box,
31247
+ #ppm-d-bad-ink:checked~.pp-disp label[for="ppm-d-bad-ink"] .pp-disp-box,
31248
+ #ppm-d-bold:checked~.pp-disp label[for="ppm-d-bold"] .pp-disp-box,
31249
+ #ppm-d-hs:checked~.pp-disp label[for="ppm-d-hs"] .pp-disp-box{background:var(--lu);border-color:var(--lu);box-shadow:inset 0 0 0 2px var(--card)}
31250
+ /* 组1 飞星派显示(默认全勾,取消才隐藏) */
31251
+ #ppm-d-ss:not(:checked)~.pp-body .pp-ss-txt{display:none}
31252
+ #ppm-d-sbg:not(:checked)~.pp-body .pp-st.st-sbg{display:none}
31253
+ #ppm-d-zha:not(:checked)~.pp-body .pp-zha{display:none}
31254
+ #ppm-d-zha:not(:checked)~.pp-zhlegend{display:none}
31255
+ #ppm-d-ages:not(:checked)~.pp-body .pp-ages{display:none}
31256
+ #ppm-d-sizhu:not(:checked)~.pp-body .ct-t3{display:none}
31257
+ /* 非节气四柱行:默认隐藏,勾 ppm-d-njq 才显示(纯增强,不改默认观感) */
31258
+ .ct-t3nj{font-size:11.5px;letter-spacing:.2em;font-weight:500;color:var(--muted)}
31259
+ .ct-t3nj .nj-tag{font-style:normal;letter-spacing:.08em;font-size:10px;color:var(--lu);border:1px solid currentColor;border-radius:3px;padding:0 3px;margin-left:6px;vertical-align:1px}
31260
+ #ppm-d-njq:not(:checked)~.pp-body .ct-t3nj{display:none}
31261
+ /* 太极点转换信息:详情面板「太极转换」行,默认隐藏,勾 ppm-d-tjz 才显示 */
31262
+ #ppm-d-tjz:not(:checked)~.pp-body .pp-detail .dt-taiji{display:none}
31263
+ /* 组2 四化派显示(默认全勾,取消才隐藏) */
31264
+ #ppm-d-small:not(:checked)~.pp-body .pp-st.aux{display:none}
31265
+ #ppm-d-shen:not(:checked)~.pp-body .pp-shen::after{display:none}
31266
+ #ppm-d-hua:not(:checked)~.pp-body .pp-m{display:none}
31267
+ #ppm-d-sf:not(:checked)~.pp-body .pp-cell.pp-tri{background:var(--bg)}
31268
+ #ppm-d-sf:not(:checked)~.pp-body .pp-cell.pp-link{background:var(--bg);box-shadow:none}
31269
+ #ppm-d-sf:not(:checked)~.pp-body svg.pp-sf{display:none}
31270
+ /* 组3 限流盘增强(默认不勾,勾了才生效;不改默认盘观感) */
31271
+ #ppm-d-zha-bold:checked~.pp-body .pp-zha .z-shaft{stroke-width:2.8}
31272
+ #ppm-d-bad-ink:checked~.pp-body .pp-st.bad{color:var(--ink)}
31273
+ #ppm-d-bad-ink:checked~.pp-body .pp-st.bad::before{content:none}
31274
+ #ppm-d-bold:checked~.pp-body .pp-st.main,#ppm-d-bold:checked~.pp-body .pp-st.good{font-weight:700}
30851
31275
  `;
30852
31276
  const pp = (window.__PP = window.__PP || {});
30853
31277
  pp.CSS = CSS;
30854
31278
 
30855
31279
  const TYPE_CLASS = { 主星: 'main', 吉星: 'good', 煞星: 'bad', 辅星: 'aux', 杂曜: 'aux' };
30856
31280
  const MUT = { 禄: 'lu', 权: 'quan', 科: 'ke', 忌: 'ji' };
31281
+ // 「显示三台八座恩光天贵」专用:这四颗是日系吉星,引擎给它们 type='吉星',
31282
+ // 与其它吉星共用 .pp-st.good。只给这四颗追加 st-sbg 供 CSS 单独显隐;
31283
+ // 其余星(尤其十四主星)的 class 串保持字面不变,别动 upgrade.test.mjs 的顺序断言。
31284
+ const SBG = new Set(['三台', '八座', '恩光', '天贵']);
30857
31285
  // 庙旺得利平不陷 → 三档视觉权重:吉(庙旺)/ 中(得利平)/ 忌(不陷)
30858
31286
  const BR_CLASS = { 庙: 'good', 旺: 'good', 得: 'mid', 利: 'mid', 平: 'mid', 不: 'bad', 陷: 'bad' };
30859
31287
  const POS = {
@@ -30936,9 +31364,11 @@ window.__MP_VER = "0.64.0";
30936
31364
  // 所以读作「←这笔化飞回本宫」而不是「从这颗星飞出去」。放星名前是关键:
30937
31365
  // 放星名后箭头朝右,视觉上就成了「飞出本宫」,意思正好反了
30938
31366
  const zh = (selfHuaMap && selfHuaMap[s.name]) ? zhaMark(selfHuaMap[s.name], s.name) : '';
31367
+ // 日系四吉星单独可寻址(文墨「显示三台八座恩光天贵」);其余星 class 串一字不改
31368
+ const extra = SBG.has(s.name) ? ' st-sbg' : '';
30939
31369
  // 顺序:自化箭头 → 星名 → 亮度灰字 → 四化色块。灰字紧贴星名变成一体,彩色实心框独立在末尾,
30940
31370
  // 避免两者字号差导致视觉叠压(试过 星名→四化→亮度,10px 字形会压住 8.5px 的框)
30941
- return `<span class="pp-st ${cls}">${zh}${s.name}${b}${tm}${m}${vm}</span>`;
31371
+ return `<span class="pp-st ${cls}${extra}">${zh}${s.name}${b}${tm}${m}${vm}</span>`;
30942
31372
  }).join('');
30943
31373
  }
30944
31374
 
@@ -30953,13 +31383,14 @@ window.__MP_VER = "0.64.0";
30953
31383
  const fx = (n) => ((n % 12) + 12) % 12;
30954
31384
  const isTrine = (b, m) => b !== m && [fx(m + 4), fx(m + 8), fx(m + 6)].includes(b);
30955
31385
 
30956
- function cellHTML(p, soulIdx, view, allPalaces, viewHua, flyByBranch) {
31386
+ function cellHTML(p, soulIdx, view, allPalaces, viewHua, flyByBranch, hsByBranch, laiyinBranch) {
30957
31387
  const pos = POS[p.branch] || [1, 1];
30958
31388
  const b = BR.indexOf(p.branch);
30959
31389
  const cls = ['pp-cell'];
30960
31390
  if (b === soulIdx) cls.push('pp-soul');
30961
31391
  else if (isTrine(b, soulIdx)) cls.push('pp-tri');
30962
31392
  if (p.isBody) cls.push('pp-shen');
31393
+ if (laiyinBranch && p.branch === laiyinBranch) cls.push('pp-laiyin');
30963
31394
  if (pos[0] === 4) cls.push('pp-lastrow');
30964
31395
  if (pos[1] === 4) cls.push('pp-lastcol');
30965
31396
  const ss = p.shensha || {};
@@ -30981,14 +31412,15 @@ window.__MP_VER = "0.64.0";
30981
31412
  ${borrow}
30982
31413
  <div class="pp-ages">${agesHTML(p.xiaoxian.age)}</div>
30983
31414
  ${fxLineHTML(p, flyByBranch)}
31415
+ ${hsLineHTML(p, hsByBranch)}
30984
31416
  <div class="pp-foot">
30985
31417
  <div class="ft-l">
30986
- <span class="ft-dx">${ss.boshi} ${p.daxian.start}-${p.daxian.end}</span>
30987
- <span>${ss.jiangqian}</span>
30988
- <span>${ss.suiqi}</span>
31418
+ <span class="ft-dx"><span class="pp-ss-txt">${ss.boshi}</span> ${p.daxian.start}-${p.daxian.end}</span>
31419
+ <span class="pp-ss-txt">${ss.jiangqian}</span>
31420
+ <span class="pp-ss-txt">${ss.suiqi}</span>
30989
31421
  </div>
30990
31422
  <div class="ft-r">
30991
- <span>${ss.changsheng}</span>
31423
+ <span class="pp-ss-txt">${ss.changsheng}</span>
30992
31424
  <span>${p.ganzhi}</span>
30993
31425
  <span class="ft-pn">${pname}</span>
30994
31426
  </div>
@@ -31030,6 +31462,16 @@ window.__MP_VER = "0.64.0";
31030
31462
  return `<div class="pp-fx">${esc(p.ganzhi)}宫干 ${items}</div>`;
31031
31463
  }
31032
31464
 
31465
+ /* 运曜/流曜小字行(文墨天机「显示运曜和流曜」):宫格内一行小字,形如「运禄巳 运羊午 …」。
31466
+ 大限视图传该宫的 yunStars({星名:地支}),流年视图传流X 键值对;默认隐藏,勾选 ppm-d-hs 才显示。
31467
+ 返回空串时不占位(本命视图、无数据时不渲染)。 */
31468
+ function hsLineHTML(p, hsByBranch) {
31469
+ const o = (hsByBranch && hsByBranch[p.branch]) || null;
31470
+ if (!o) return '';
31471
+ const items = Object.entries(o).map(([k, br]) => `<i>${esc(k)}<em>${esc(br)}</em></i>`).join('');
31472
+ return `<div class="pp-hs">${items}</div>`;
31473
+ }
31474
+
31033
31475
  function sizhuOf(r) {
31034
31476
  try {
31035
31477
  const E = window.Engine || (window.__PP && window.__PP.Engine);
@@ -31043,6 +31485,25 @@ window.__MP_VER = "0.64.0";
31043
31485
  } catch (e) { return ''; }
31044
31486
  }
31045
31487
 
31488
+ /* 日干四化:取日柱天干,查十干四化表,四颗化星按本命落宫归堆。
31489
+ 口径:日干起四化(文墨飞星派「显示日干四化」);表用 sihuaTableOf 的通行表
31490
+ 这里无法直接调引擎内部表,改用宫干自化同源法——宫干 = 生年干那颗的四化已由
31491
+ mutagenMarks 体现,日干四化是另一层。实现:window.Engine 不暴露 sihuaTableOf,
31492
+ 改从宫干四化反推——十二宫宫干覆盖十干(寅申两支各占两干),日干对应的宫干四化
31493
+ 可从 daxianList[].mutagenHits(按宫干分组)直接借。 */
31494
+ function dayStemOf(r) {
31495
+ try {
31496
+ const E = window.Engine || (window.__PP && window.__PP.Engine);
31497
+ if (!E || !E.buildBazi || !r.input) return null;
31498
+ const b = E.buildBazi({
31499
+ year: r.input.year, month: r.input.month, day: r.input.day,
31500
+ hour: r.input.hour, minute: r.input.minute,
31501
+ gender: r.input.gender, trueSolar: false,
31502
+ });
31503
+ return b.pillars.day.stem;
31504
+ } catch (e) { return null; }
31505
+ }
31506
+
31046
31507
  pp.ziweiHTML = function (r) {
31047
31508
  const soulIdx = BR.indexOf(r.soul.branch);
31048
31509
  const ln = (r.liunian && r.liunian[0]) || null;
@@ -31062,7 +31523,51 @@ window.__MP_VER = "0.64.0";
31062
31523
 
31063
31524
  const flyByBranch = {};
31064
31525
  for (const d of r.daxianList || []) flyByBranch[d.branch] = d.mutagenHits || [];
31065
- const grid = (m, kind) => r.palaces.map((p) => cellHTML(p, m, kind, r.palaces, huaByView[kind] || null, flyByBranch)).join('');
31526
+
31527
+ // 日干四化:借宫干四化反推——daxianList[].mutagenHits 按「宫干→四化星」分组,
31528
+ // 找到宫干 === 日干的那一宫,其 mutagenHits 就是日干四化(同一张十干四化表)。
31529
+ // 星 → 落宫:palaces[].stars 里找星名。
31530
+ const riGanHits = (() => {
31531
+ const ds = dayStemOf(r);
31532
+ if (!ds) return null;
31533
+ const hit = (r.daxianList || []).find((d) => d.stem === ds);
31534
+ if (!hit) return null;
31535
+ const starBranch = {};
31536
+ for (const p of r.palaces) for (const s of p.stars || []) if (!(s.name in starBranch)) starBranch[s.name] = p.branch;
31537
+ return {
31538
+ stem: ds,
31539
+ items: (hit.mutagenHits || []).map((h) => ({
31540
+ type: h.type, star: h.star,
31541
+ branch: starBranch[h.star] || null,
31542
+ palace: starBranch[h.star] ? (r.palaces.find((p) => p.branch === starBranch[h.star]) || {}).palace : null,
31543
+ })),
31544
+ };
31545
+ })();
31546
+ // 运曜/流曜:按宫位摊。大限视图用 daxianList[].yunStars({星名:地支}),流年视图用 ln 的 流X/年解 键。
31547
+ let lnHsByBranch = null;
31548
+ if (ln) {
31549
+ lnHsByBranch = {};
31550
+ for (const [k, br] of Object.entries(ln)) if (/^(流|年解)/.test(k) && typeof br === 'string' && BR.includes(br)) lnHsByBranch[br] = lnHsByBranch[br] || {}, lnHsByBranch[br][k] = br;
31551
+ }
31552
+ const hsByView = {
31553
+ da: dxObj && dxObj.yunStars
31554
+ ? (() => {
31555
+ const o = {};
31556
+ for (const [k, br] of Object.entries(dxObj.yunStars)) (o[br] = o[br] || {})[k] = br;
31557
+ // 限流十二神(随大限宫支重安的岁前/将前)并入同一小字行
31558
+ if (dxObj.yunSuiqi) for (const [k, br] of Object.entries(dxObj.yunSuiqi)) (o[br] = o[br] || {})[k] = br;
31559
+ if (dxObj.yunJiangqian) for (const [k, br] of Object.entries(dxObj.yunJiangqian)) (o[br] = o[br] || {})[k] = br;
31560
+ return o;
31561
+ })()
31562
+ : null,
31563
+ ln: lnHsByBranch,
31564
+ };
31565
+ if (r.liunian && r.liunian.length) {
31566
+ const ln0 = r.liunian.find((x) => x.ganzhi === (r._lnSel || r.liunian[0].ganzhi)) || r.liunian[0];
31567
+ if (ln0.lnSuiqi) for (const [k, br] of Object.entries(ln0.lnSuiqi)) (lnHsByBranch[br] = lnHsByBranch[br] || {})[k] = br;
31568
+ if (ln0.lnJiangqian) for (const [k, br] of Object.entries(ln0.lnJiangqian)) (lnHsByBranch[br] = lnHsByBranch[br] || {})[k] = br;
31569
+ }
31570
+ const grid = (m, kind) => r.palaces.map((p) => cellHTML(p, m, kind, r.palaces, huaByView[kind] || null, flyByBranch, hsByView[kind] || null, r.laiyin && r.laiyin.branch)).join('');
31066
31571
  const center = (m, kind) => {
31067
31572
  const mode = kind === 'da'
31068
31573
  ? `<div class="ct-t1">大限 ${dxPalace.ganzhi} ${dxPalace.daxian.start}-${dxPalace.daxian.end}岁</div>`
@@ -31074,6 +31579,7 @@ window.__MP_VER = "0.64.0";
31074
31579
  <div class="ct-t2">公历 ${r.input.year}-${String(r.input.month).padStart(2, '0')}-${String(r.input.day).padStart(2, '0')} ${String(r.input.hour).padStart(2, '0')}:${String(r.input.minute).padStart(2, '0')}</div>
31075
31580
  <div class="ct-t2">农历 ${r.lunar.year}年${r.lunar.isLeap ? `闰${r.lunar.monthRaw || r.lunar.month}` : r.lunar.month}月${r.lunar.day}日${r.hourBranch}时${r.lunar.isLeap && r.lunar.monthRaw && r.lunar.month !== r.lunar.monthRaw ? `(按${r.lunar.month}月起盘)` : ''}</div>
31076
31581
  ${sizhuOf(r) ? `<div class="ct-t3">${sizhuOf(r)}</div>` : ''}
31582
+ ${r.nonJieqi ? `<div class="ct-t3 ct-t3nj" title="${esc(r.nonJieqi.note)}">${r.nonJieqi.year} ${r.nonJieqi.month} ${r.nonJieqi.day} ${r.nonJieqi.hour}<em class="nj-tag">非节气</em></div>` : ''}
31077
31583
  <div class="ct-t4">命主 ${r.lifeMaster}${r.lifeMasterNote ? `<span class="pp-dim" title="${esc(r.lifeMasterNote)}">*</span>` : ''} 身主 ${r.bodyMaster}${r.bodyMasterNote ? `<span class="pp-dim" title="${esc(r.bodyMasterNote)}">*</span>` : ''}</div>
31078
31584
  <div class="ct-t4">命宫 ${r.soul.branch} 身宫 ${r.body.branch} 五行局 ${r.bureau.name}</div>
31079
31585
  <div class="ct-t4">子年斗君 ${r.doujun ? r.doujun.zinian : '—'} 流年斗君 ${ln && ln.doujun ? ln.doujun : '—'}</div>
@@ -31163,6 +31669,51 @@ window.__MP_VER = "0.64.0";
31163
31669
  <input type="radio" name="ppm-sc" id="ppm-sc-sihe" class="pp-r" checked>
31164
31670
  <input type="radio" name="ppm-sc" id="ppm-sc-feixing" class="pp-r">
31165
31671
  <input type="radio" name="ppm-sc" id="ppm-sc-sanhe" class="pp-r">
31672
+ <!-- 文墨式显示设置:14 个复选框必须与 .pp-body / .pp-zhlegend 同层(.pp 的直接子元素)。
31673
+ 组1/组2 默认勾(观感与旧版一致),组3 默认不勾(纯增强)。不重排盘、不进引擎。 -->
31674
+ <input type="checkbox" id="ppm-d-ss" class="pp-r" checked>
31675
+ <input type="checkbox" id="ppm-d-sbg" class="pp-r" checked>
31676
+ <input type="checkbox" id="ppm-d-zha" class="pp-r" checked>
31677
+ <input type="checkbox" id="ppm-d-ages" class="pp-r" checked>
31678
+ <input type="checkbox" id="ppm-d-sizhu" class="pp-r" checked>
31679
+ <input type="checkbox" id="ppm-d-small" class="pp-r" checked>
31680
+ <input type="checkbox" id="ppm-d-shen" class="pp-r" checked>
31681
+ <input type="checkbox" id="ppm-d-hua" class="pp-r" checked>
31682
+ <input type="checkbox" id="ppm-d-sf" class="pp-r" checked>
31683
+ <input type="checkbox" id="ppm-d-laiyin" class="pp-r" checked>
31684
+ <input type="checkbox" id="ppm-d-zha-bold" class="pp-r">
31685
+ <input type="checkbox" id="ppm-d-bad-ink" class="pp-r">
31686
+ <input type="checkbox" id="ppm-d-bold" class="pp-r">
31687
+ <input type="checkbox" id="ppm-d-hs" class="pp-r">
31688
+ <input type="checkbox" id="ppm-d-rigan" class="pp-r">
31689
+ <input type="checkbox" id="ppm-d-njq" class="pp-r">
31690
+ <input type="checkbox" id="ppm-d-tjz" class="pp-r">
31691
+ <details class="pp-disp">
31692
+ <summary>显示设置 · 17 项(学文墨天机)</summary>
31693
+ <div class="pp-disp-g"><span>飞星派显示</span>
31694
+ <label for="ppm-d-ss"><i class="pp-disp-box"></i>显示神煞</label>
31695
+ <label for="ppm-d-sbg"><i class="pp-disp-box"></i>三台八座恩光天贵</label>
31696
+ <label for="ppm-d-zha"><i class="pp-disp-box"></i>彩色箭头标识自化</label>
31697
+ <label for="ppm-d-ages"><i class="pp-disp-box"></i>流年虚岁年龄</label>
31698
+ <label for="ppm-d-sizhu"><i class="pp-disp-box"></i>中宫八字</label>
31699
+ <label for="ppm-d-rigan"><i class="pp-disp-box"></i>显示日干四化</label>
31700
+ <label for="ppm-d-njq"><i class="pp-disp-box"></i>显示非节气四柱</label>
31701
+ <label for="ppm-d-tjz"><i class="pp-disp-box"></i>显示太极点转换信息</label>
31702
+ </div>
31703
+ <div class="pp-disp-g"><span>四化派显示</span>
31704
+ <label for="ppm-d-small"><i class="pp-disp-box"></i>显示小星</label>
31705
+ <label for="ppm-d-shen"><i class="pp-disp-box"></i>显示身宫</label>
31706
+ <label for="ppm-d-hua"><i class="pp-disp-box"></i>显示命宫四化</label>
31707
+ <label for="ppm-d-sf"><i class="pp-disp-box"></i>显示三方四正</label>
31708
+ <label for="ppm-d-laiyin"><i class="pp-disp-box"></i>显示来因宫</label>
31709
+ </div>
31710
+ <div class="pp-disp-g"><span>限流盘显示</span>
31711
+ <label for="ppm-d-zha-bold"><i class="pp-disp-box"></i>自化箭头加粗</label>
31712
+ <label for="ppm-d-bad-ink"><i class="pp-disp-box"></i>六煞星黑字</label>
31713
+ <label for="ppm-d-bold"><i class="pp-disp-box"></i>主辅星加粗</label>
31714
+ <label for="ppm-d-hs"><i class="pp-disp-box"></i>显示运曜和流曜</label>
31715
+ </div>
31716
+ </details>
31166
31717
  <div class="pp-modes">
31167
31718
  <label for="ppm-ben">本命</label><label for="ppm-da">大限</label><label for="ppm-ln">流年</label>
31168
31719
  <span class="pp-soon">点任一宫位看详情</span>
@@ -31173,6 +31724,21 @@ window.__MP_VER = "0.64.0";
31173
31724
  ${body('ben', soulIdx, 'ben')}
31174
31725
  ${body('da', dxIdx, 'da')}
31175
31726
  ${body('ln', lnIdx, 'ln')}
31727
+ ${(() => {
31728
+ if (!riGanHits) return '';
31729
+ const CLS = { 禄: 'rl', 权: 'rq', 科: 'rk', 忌: 'rj' };
31730
+ const items = riGanHits.items
31731
+ .map((h) => `<i><em class="${CLS[h.type] || ''}">${h.type}</em>${esc(h.star)}<em>${esc(h.branch || '?')}${h.palace ? `·${esc(h.palace)}` : ''}</em></i>`)
31732
+ .join('');
31733
+ return `<div class="pp-rigan"><b>日干${esc(riGanHits.stem)}四化</b>${items}</div>`;
31734
+ })()} ${(() => {
31735
+ if (!riGanHits) return '';
31736
+ const CLS = { 禄: 'rl', 权: 'rq', 科: 'rk', 忌: 'rj' };
31737
+ const items = riGanHits.items
31738
+ .map((h) => `<i><em class="${CLS[h.type] || ''}">${h.type}</em>${esc(h.star)}<em>${esc(h.branch || '?')}${h.palace ? `·${esc(h.palace)}` : ''}</em></i>`)
31739
+ .join('');
31740
+ return `<div class="pp-rigan"><b>日干${esc(riGanHits.stem)}四化</b>${items}</div>`;
31741
+ })()}
31176
31742
  <div class="pp-zhlegend">自化图示 <span class="z-lu">${pp._zhaMark('禄', '本宫')}禄</span><span class="z-quan">${pp._zhaMark('权', '本宫')}权</span><span class="z-ke">${pp._zhaMark('科', '本宫')}科</span><span class="z-ji">${pp._zhaMark('忌', '本宫')}忌</span></div>
31177
31743
  <div class="pp-flywrap">${flyHTML}${flyCompositeHTML}</div>
31178
31744
  </div>${flowHTML}${chips}${pats}${dxs}${lnHit}${zyHTML}`;
@@ -31224,6 +31790,14 @@ window.__MP_VER = "0.64.0";
31224
31790
  ? starLine(mains)
31225
31791
  : `<span class="dt-empty">无主星</span><span class="dt-tri"> 借对宫 <b>${borrowStars.length ? borrowStars.map((s) => s.name).join('、') : '亦无主星'}</b></span>`;
31226
31792
  const row = (k, v) => `<div class="dt-dr"><span class="dt-k">${k}</span><div class="dt-v">${v}</div></div>`;
31793
+ // 太极点转换(文墨「显示太极点转换信息」):以本宫为新命宫重布十二宫名。
31794
+ // 宫序顺时针与本命同名序一致——新命宫=本宫,其后按本命十二宫顺序依次重排。
31795
+ const N = NAMES.length;
31796
+ const pivotOffset = fx(soulIdx - b); // 本宫在本命盘的宫名序
31797
+ const taiji = NAMES.map((_, i) => {
31798
+ const idx = fx(b + (i - pivotOffset + N) % N); // 从本宫起,按宫名序号推地支
31799
+ return `<span class="dt-st">${NAMES[i]}[${BR[idx]}]</span>`;
31800
+ }).join('');
31227
31801
 
31228
31802
  return `<div class="dt-dh">
31229
31803
  <b>${nm}</b><span>${p.branch}宫 · ${p.ganzhi}</span>
@@ -31239,6 +31813,7 @@ window.__MP_VER = "0.64.0";
31239
31813
  ${row('宫干四化', selfHua)}
31240
31814
  ${row('十二神', `<span class="dt-tri">长生 ${ss.changsheng || '—'} 博士 ${ss.boshi || '—'} 将前 ${ss.jiangqian || '—'} 岁前 ${ss.suiqi || '—'}</span>`)}
31241
31815
  ${row('三方四正', `<span class="dt-tri">${tri}</span>`)}
31816
+ ${row('太极转换', `<span class="dt-tri dt-taiji">以${nm}[${p.branch}]立太极${taiji}</span>`)}
31242
31817
  <div class="hint">三方四正在盘上已同步标出(浅红描边)。再点一次本宫可收起。</div>`;
31243
31818
  }
31244
31819