dsh-metaphysics 0.50.1 → 0.50.5

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.
package/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  window.__ModuleLoader__.load({
2
2
  id: 'dsh-metaphysics',
3
- version: '0.50.1',
3
+ version: '0.50.5',
4
4
  factory(require) {
5
5
  const React = (function () {
6
6
  try { return require('react'); } catch (e) { return globalThis.React; }
@@ -19,6 +19,277 @@ const fail = (code, message) => {
19
19
  return { fail };
20
20
  })();
21
21
 
22
+ __mods.diff = (function () {
23
+
24
+ const IGNORE_KEYS = new Set([
25
+ 'summary', 'interpret', 'castAt', 'asOf', 'precision', 'disclaimer', 'note',
26
+ 'trueSolarText', 'civilText', 'offsetMinutes',
27
+ 'input', // 只是把调用参数回显出来,不是盘面变化
28
+ 'jd', // 内部儒略日,人类不可读;对外展示用 time 等格式化字段
29
+ 'juMethod', 'juMethodNote', 'lateZiHourMode',
30
+ 'leapPolicy',
31
+ ]);
32
+
33
+ const isObject = (v) => v !== null && typeof v === 'object';
34
+
35
+ function diffObjects(a, b, path = '', out = []) {
36
+ if (a === b) return out;
37
+ if (!isObject(a) || !isObject(b)) {
38
+ out.push({ path: path || '(root)', from: a, to: b });
39
+ return out;
40
+ }
41
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
42
+ for (const k of keys) {
43
+ if (IGNORE_KEYS.has(k)) continue;
44
+ const p = path ? `${path}.${k}` : k;
45
+ const va = a[k];
46
+ const vb = b[k];
47
+ if (isObject(va) && isObject(vb)) {
48
+ diffObjects(va, vb, p, out);
49
+ } else if (JSON.stringify(va) !== JSON.stringify(vb)) {
50
+ out.push({ path: p, from: va, to: vb });
51
+ }
52
+ }
53
+ return out;
54
+ }
55
+
56
+ const LABELS = [
57
+ [/^pillars\.year\.ganzhi$/, '年柱'],
58
+ [/^pillars\.month\.ganzhi$/, '月柱'],
59
+ [/^pillars\.day\.ganzhi$/, '日柱'],
60
+ [/^pillars\.hour\.ganzhi$/, '时柱'],
61
+ [/^pillars\.\w+\.hidden/, '藏干'],
62
+ [/^pillars\.\w+\.stemTenGod$/, '十神'],
63
+ [/^pillars\.\w+\.kongwang/, '空亡'],
64
+ [/^pillars\.\w+\.dishi$|^pillars\.\w+\.zizuo$/, '十二长生'],
65
+ [/^pillars\.\w+\.nayin$/, '纳音'],
66
+ [/^dayMaster\./, '日主'],
67
+ [/^palaces\.(taiyuan|minggong|shengong)\./, '命身宫'],
68
+ [/^palaces\.\d+\.(palace|branch|stem|ganzhi)$/, '宫位'],
69
+ [/^palaces\.\d+\.stars\.\d+\.(name|brightness|type)$/, '星曜'],
70
+ [/^palaces\.\d+\.shensha\./, '神煞'],
71
+ [/^palaces\.\d+\.(daxian|xiaoxian)\./, '大限'],
72
+ [/^palaces\.\d+\.sanfang\./, '三方四正'],
73
+ [/^palaces\.\d+\./, '宫位'],
74
+ [/^palaces\./, '命身宫'],
75
+ [/^soul\./, '命宫'],
76
+ [/^body\./, '身宫'],
77
+ [/^doujun\./, '流年斗君'],
78
+ [/^siZhu\.year$/, '年柱'],
79
+ [/^siZhu\.month$/, '月柱'],
80
+ [/^siZhu\.day$/, '日柱'],
81
+ [/^siZhu\.time$/, '时柱'],
82
+ [/^ju$|^juNumber$/, '局数'],
83
+ [/^yuan$/, '三元'],
84
+ [/^dun$/, '阴阳遁'],
85
+ [/^daxian(List)?\./, '大限'],
86
+ [/^fly(Chains|Composite)?\./, '飞星'],
87
+ [/^pattern/, '格局'],
88
+ [/^schools\./, '流派回显'],
89
+ [/^dayun\.jiao\./, '起运'],
90
+ [/^dayun\.toNextJieDays$/, '起运时间'],
91
+ [/^dayun\.list\.(\d+)\.ganzhi$/, '大运干支'],
92
+ [/^dayun\.startAge\./, '起运时间'],
93
+ [/^dayun\./, '大运评分'],
94
+ [/^liunian\./, '流年'],
95
+ [/^jieqi\./, '节气边界'],
96
+ [/^solarTime\./, '真太阳时'],
97
+ [/^dayGanZhi$/, '日辰'],
98
+ [/^hourZhi$/, '时支'],
99
+ [/^yueJiang\./, '月将'],
100
+ [/^xunKong$/, '旬空'],
101
+ [/^lines\.(\d+)\./, '爻'],
102
+ [/^shensha\./, '神煞'],
103
+ [/^frame\./, '格局用神'],
104
+ [/^strength\./, '旺衰'],
105
+ [/^wuxing\./, '五行计数'],
106
+ [/^relations\./, '干支关系'],
107
+ [/^input\./, '输入回显'],
108
+ ];
109
+
110
+ const CATEGORY = [
111
+ [/^pillars\./, '四柱'],
112
+ [/^shensha\./, '神煞'],
113
+ [/^dayun\./, '大运'],
114
+ [/^liunian\./, '流年'],
115
+ [/^frame\./, '格局用神'],
116
+ [/^strength\./, '旺衰'],
117
+ [/^wuxing\./, '五行计数'],
118
+ [/^dayMaster\./, '日主'],
119
+ [/^relations\./, '干支关系'],
120
+ [/^palaces\.(taiyuan|minggong|shengong)\./, '命身宫'],
121
+ [/^palaces\.\d+\./, '宫位'],
122
+ [/^palaces\./, '命身宫'],
123
+ [/^soul\.|^body\.|^doujun\./, '命身宫'],
124
+ [/^siZhu\./, '四柱'],
125
+ [/^ju$|^juNumber$|^yuan$|^dun$/, '局数'],
126
+ [/^daxian/, '大限'],
127
+ [/^fly/, '飞星'],
128
+ [/^pattern/, '格局'],
129
+ [/^jieqi\./, '节气'],
130
+ [/^solarTime\./, '真太阳时'],
131
+ [/^input\./, '输入回显'],
132
+ [/^lines\./, '爻'],
133
+ ];
134
+
135
+ const ROOT = [
136
+ [/^pillars\.(year|month|day|hour)\.ganzhi$/, '柱'],
137
+ [/^palaces\.(minggong|shengong)\.ganzhi$/, '命身宫'],
138
+ [/^soul\.ganzhi$/, '命宫'],
139
+ [/^body\.branch$/, '身宫'],
140
+ [/^siZhu\.(year|month|day|time)$/, '柱'],
141
+ [/^dayun\.jiao\.at$/, '起运'],
142
+ [/^dayGanZhi$/, '日辰'],
143
+ [/^hourZhi$/, '时支'],
144
+ [/^ju$|^juNumber$|^juName$/, '局数'],
145
+ [/^dun$/, '阴阳遁'],
146
+ [/^lines\.\d+\.(ganzhi|yinYang|name)$/, '爻'],
147
+ [/^yueJiang\.(name|zhi)$/, '月将'],
148
+ ];
149
+
150
+ function labelOf(path) {
151
+ for (const [re, label] of LABELS) {
152
+ if (re.test(path)) return label;
153
+ }
154
+ return categoryOf(path) || path;
155
+ }
156
+
157
+ function categoryOf(path) {
158
+ for (const [re, cat] of CATEGORY) {
159
+ if (re.test(path)) return cat;
160
+ }
161
+ return '';
162
+ }
163
+
164
+ function rootOf(path) {
165
+ for (const [re, label] of ROOT) {
166
+ if (re.test(path)) return label;
167
+ }
168
+ return '';
169
+ }
170
+
171
+ function sortDiffs(diffs) {
172
+ return [...diffs].sort((x, y) => x.path.localeCompare(y.path, 'zh-Hans-CN', { numeric: true }));
173
+ }
174
+
175
+ function summarize(diffs) {
176
+ if (!diffs.length) return '本盘不受此口径影响:所有字段一致。';
177
+ const roots = diffs.filter((d) => rootOf(d.path));
178
+ const cats = [...new Set(diffs.map((d) => categoryOf(d.path)).filter(Boolean))];
179
+ const head = roots.length
180
+ ? `根因 ${roots.length} 处:` + roots.slice(0, 4)
181
+ .map((d) => `${labelOf(d.path)} ${formatValue(d.from)}→${formatValue(d.to)}`).join('、')
182
+ + (roots.length > 4 ? ' 等' : '')
183
+ : '无单一根因';
184
+ const tail = cats.length
185
+ ? `;连带影响 ${cats.slice(0, 6).join('、')}${cats.length > 6 ? ' 等' : ''} ${cats.length} 类,共 ${diffs.length} 处`
186
+ : `;共 ${diffs.length} 处`;
187
+ return head + tail + '。';
188
+ }
189
+
190
+ const formatValue = (v) => {
191
+ if (v === undefined) return '—';
192
+ if (v === null) return 'null';
193
+ if (typeof v === 'string') return v;
194
+ if (Array.isArray(v)) return v.join('') || '[]';
195
+ if (typeof v === 'object') return JSON.stringify(v);
196
+ return String(v);
197
+ };
198
+
199
+ function renderDiff(diffs, { title = '', limit = 12 } = {}) {
200
+ const lines = [];
201
+ if (title) lines.push(`【${title}】`);
202
+ lines.push(summarize(diffs));
203
+ if (!diffs.length) return lines.join('\n');
204
+
205
+ const roots = sortDiffs(diffs.filter((d) => rootOf(d.path)));
206
+ const rest = sortDiffs(diffs.filter((d) => !rootOf(d.path)));
207
+
208
+ if (roots.length) {
209
+ lines.push('');
210
+ lines.push(' 根因(口径直接改动):');
211
+ for (const d of roots) {
212
+ lines.push(` ${labelOf(d.path).padEnd(6)} ${formatValue(d.from)} → ${formatValue(d.to)}`);
213
+ }
214
+ }
215
+ if (rest.length) {
216
+ lines.push('');
217
+ lines.push(` 连带变化(${rest.length} 处,展示前 ${Math.min(limit, rest.length)} 处):`);
218
+ for (const d of rest.slice(0, limit)) {
219
+ lines.push(` ${labelOf(d.path).padEnd(6)} ${formatValue(d.from)} → ${formatValue(d.to)}`);
220
+ }
221
+ if (rest.length > limit) {
222
+ const byCat = {};
223
+ for (const d of rest.slice(limit)) {
224
+ const c = categoryOf(d.path) || '其他';
225
+ byCat[c] = (byCat[c] || 0) + 1;
226
+ }
227
+ lines.push(' 其余:' + Object.entries(byCat).map(([c, n]) => `${c} ${n} 处`).join('、'));
228
+ }
229
+ }
230
+ return lines.join('\n');
231
+ }
232
+
233
+ const CALIBER_SWITCHES = {
234
+ bazi: [
235
+ {
236
+ key: 'lateZi',
237
+ title: '晚子时:23:00 后是否换日',
238
+ short: '晚子时',
239
+ read: (a) => a.lateZi !== false,
240
+ alt: (v) => !v,
241
+ label: (v) => (v ? '换日' : '不换日'),
242
+ },
243
+ {
244
+ key: 'trueSolar',
245
+ title: '真太阳时:是否按出生地经度修正',
246
+ short: '真太阳时',
247
+ read: (a) => a.trueSolar !== false,
248
+ alt: (v) => !v,
249
+ label: (v) => (v ? '修正' : '不修正'),
250
+ },
251
+ ],
252
+ ziwei: [
253
+ {
254
+ key: 'leapMonth',
255
+ title: '闰月归月:按日切分 / 整月归次月',
256
+ short: '闰月归月',
257
+ read: (a) => (a.leapMonth === 'next' || a.leapMonth === 'prev' ? a.leapMonth : 'split'),
258
+ alt: (v) => (v === 'split' ? 'next' : 'split'),
259
+ label: (v) => ({ split: '按日切分', next: '整月归次月', prev: '整月归本月' }[v] || v),
260
+ },
261
+ {
262
+ key: 'trueSolar',
263
+ title: '真太阳时:是否按出生地经度修正',
264
+ short: '真太阳时',
265
+ read: (a) => a.trueSolar !== false,
266
+ alt: (v) => !v,
267
+ label: (v) => (v ? '修正' : '不修正'),
268
+ },
269
+ ],
270
+ qimen: [
271
+ {
272
+ key: 'juMethod',
273
+ title: '取局流派:拆补 / 超神接气 / 置闰',
274
+ short: '取局流派',
275
+ read: (a) => (['chaibu', 'chaoshen', 'zhirun'].includes(a.juMethod) ? a.juMethod : 'chaibu'),
276
+ alt: (v) => (v === 'chaibu' ? 'zhirun' : 'chaibu'),
277
+ label: (v) => ({ chaibu: '拆补法', chaoshen: '超神接气', zhirun: '置闰法' }[v] || v),
278
+ },
279
+ {
280
+ key: 'lateZiHourMode',
281
+ title: '晚子时:23:00 后归当日 / 次日',
282
+ short: '晚子时归日',
283
+ read: (a) => (a.lateZiHourMode === 'next-day' ? 'next-day' : 'same-day'),
284
+ alt: (v) => (v === 'next-day' ? 'same-day' : 'next-day'),
285
+ label: (v) => (v === 'next-day' ? '归次日' : '归当日'),
286
+ },
287
+ ],
288
+ };
289
+
290
+ return { diffObjects, labelOf, categoryOf, rootOf, sortDiffs, summarize, formatValue, renderDiff, CALIBER_SWITCHES };
291
+ })();
292
+
22
293
  __mods.validate = (function () {
23
294
  const { fail } = __mods.fail;
24
295
 
@@ -2919,6 +3190,16 @@ const CLASSICS = [
2919
3190
  plain: '大运以生月为起点,最怕与流年(太岁)冲克:原文说岁运冲月为祸,又把「岁运与日柱相对」叫返吟、「岁运压日」叫伏吟。这两句是按「对日柱」说的,与本插件「干支相同为伏吟、天克地冲为反吟」不是同一套定义(本插件取后者);末句「不利六亲、破财」是旧说的重话,只作变动提示,不作断语。',
2920
3191
  url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7539.html',
2921
3192
  },
3193
+ {
3194
+ id: 'smt-renyuan',
3195
+ book: 'sanming',
3196
+ chapter: '卷二·论人元司事',
3197
+ scene: '人元',
3198
+ tags: [],
3199
+ quote: '故支吕所藏者主命,谓之人元,名为司事之神,以命术言之为月令。如正月建寅,寅中有艮土用事五日,丙火长生五日,甲木二十日;二月建卯,卯中有甲木用事七日,乙木二十三日;',
3200
+ plain: '地支所藏为「人元」,按月令分日轮值。三命通会这一本给的是另一套日数:寅月艮土(戊土)五日、丙火五日、甲木二十日,卯月甲七日、乙二十三日。它与本插件采用的通行 30 日表(寅月戊7丙7甲16,即 7/7/16;卯月甲10乙20,即 10/20)不是同一套分法;两表各照原样并列,本插件仍按通行表给「第几日谁当权」,不改取格。',
3201
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7529.html',
3202
+ },
2922
3203
  ];
2923
3204
 
2924
3205
  const BY_ID = CLASSICS.reduce((m, e) => { m[e.id] = e; return m; }, {});
@@ -3285,11 +3566,15 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
3285
3566
  const me = roles.me;
3286
3567
  const weak = strength.level === '身弱' || strength.level === '偏弱';
3287
3568
  const strong = strength.level === '身强' || strength.level === '偏强';
3569
+ const roleName = (el) => (el === me ? '比劫' : el === roles.inMe ? '印'
3570
+ : el === roles.shi ? '食伤' : el === roles.cai ? '财' : '官杀');
3571
+ const ROLE_VERB = { 比劫: '帮身', 印: '生身', 食伤: '泄秀', 财: '耗', 官杀: '制' };
3288
3572
 
3289
3573
  const baseFavor = weak ? [roles.inMe, me] : strong ? [roles.shi, roles.cai, roles.guan] : [roles.inMe, me, roles.shi];
3290
3574
  const baseAvoid = weak ? [roles.shi, roles.cai, roles.guan] : strong ? [me, roles.inMe] : [roles.guan, roles.cai];
3291
3575
  let favor = baseFavor.slice();
3292
3576
  let avoid = baseAvoid.slice();
3577
+ const disputed = [];
3293
3578
 
3294
3579
  const climate = judgeClimate({ monthBranch, dayStem, wuxing, branches });
3295
3580
  const qt = qtOf(dayStem, monthBranch);
@@ -3298,27 +3583,39 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
3298
3583
  const needEl = climate.need.element;
3299
3584
  if (!favor.includes(needEl)) favor.unshift(needEl);
3300
3585
  avoid = avoid.filter((e) => e !== needEl);
3301
- if (climate.avoidElement && !avoid.includes(climate.avoidElement)) avoid.unshift(climate.avoidElement);
3586
+ const badEl = climate.avoidElement;
3587
+ if (badEl && favor.includes(badEl)) {
3588
+ disputed.push({
3589
+ element: badEl,
3590
+ favorRole: roleName(badEl),
3591
+ favorVerb: ROLE_VERB[roleName(badEl)],
3592
+ climate: climate.label,
3593
+ note: `调候忌${badEl}(${climate.label}),但它在扶抑里为${roleName(badEl)};本表保留在喜用,不列入忌。`,
3594
+ });
3595
+ } else if (badEl && !avoid.includes(badEl)) {
3596
+ avoid.unshift(badEl);
3597
+ }
3302
3598
  }
3303
3599
 
3304
- const roleName = (el) => (el === me ? '比劫' : el === roles.inMe ? '印'
3305
- : el === roles.shi ? '食伤' : el === roles.cai ? '财' : '官杀');
3306
3600
  const expand = {
3307
3601
  比劫: ['比肩', '劫财'], 印: ['正印', '偏印'], 食伤: ['食神', '伤官'],
3308
3602
  财: ['正财', '偏财'], 官杀: ['正官', '七杀'],
3309
3603
  };
3310
3604
  const favorTenGods = Array.from(new Set(favor.flatMap((el) => expand[roleName(el)] || [])));
3311
3605
  const avoidTenGods = Array.from(new Set(avoid.flatMap((el) => expand[roleName(el)] || [])));
3606
+ const disputedTenGods = Array.from(new Set(disputed.flatMap((d) => expand[roleName(d.element)] || [])));
3312
3607
  const tongguan = tongguanOf(favor, avoid, wuxing);
3313
3608
 
3609
+ const describe = (el) => `${el}(${roleName(el)}·${ROLE_VERB[roleName(el)]})`;
3610
+ const disputedText = disputed
3611
+ .map((d) => `${d.element}(扶抑为${d.favorRole}·${d.favorVerb};${d.climate}盘调候忌${d.element},本表保留在喜用)`)
3612
+ .join('、');
3314
3613
  const detail = [
3315
3614
  `日主${dayStem}${me},${strength.level}(${strength.score} 分):`,
3316
- weak
3317
- ? `喜${roles.inMe}(印)生身、${me}(比劫)帮身;忌${roles.shi}(食伤)泄、${roles.cai}(财)耗、${roles.guan}(官杀)克。`
3318
- : strong
3319
- ? `喜${roles.shi}(食伤)泄秀、${roles.cai}(财)耗、${roles.guan}(官杀)制;忌${me}(比劫)与${roles.inMe}(印)再助。`
3320
- : '身中和,先取调候与通关,喜忌随大运流年动态变化。',
3321
- `调候:${climate.label}(${(climate.evidence || []).join('、')})${climate.need ? `,取${climate.need.stem}${climate.need.element},忌${climate.avoidElement}` : ',气候平和'}。`,
3615
+ `喜${favor.map(describe).join('、') || '—'};忌${avoid.map(describe).join('、') || '—'}。`,
3616
+ disputed.length ? `有争议:${disputedText}。` : '',
3617
+ !weak && !strong ? '身中和,先取调候与通关,喜忌随大运流年动态变化。' : '',
3618
+ `调候:${climate.label}(${(climate.evidence || []).join('、')})${climate.need ? `,取${climate.need.stem}${climate.need.element},忌${climate.avoidElement}${disputed.some((d) => d.element === climate.avoidElement) ? '(有争议)' : ''}` : ',气候平和'}。`,
3322
3619
  qt ? `穷通宝鉴:${qt.month}${dayStem}${me},用神${qt.yong.join('、')}(首取${qt.primary})。` : '',
3323
3620
  tongguan
3324
3621
  ? `通关:忌${tongguan.foe}克用${tongguan.use},五行以${tongguan.bridge}为关;局中${tongguan.level === '可通'
@@ -3338,10 +3635,12 @@ function yongshenOf(dayStem, monthBranch, strength, wuxing, branches = []) {
3338
3635
  level: strength.level,
3339
3636
  favor,
3340
3637
  avoid,
3638
+ disputed,
3341
3639
  favorRoles: favor.map(roleName),
3342
3640
  avoidRoles: avoid.map(roleName),
3343
3641
  favorTenGods,
3344
3642
  avoidTenGods,
3643
+ disputedTenGods,
3345
3644
  tiaohou: { ...climate, ...arb, element: climate.need ? climate.need.element : null },
3346
3645
  climate,
3347
3646
  qt,
@@ -3928,6 +4227,15 @@ function buildBazi(input) {
3928
4227
  } else if (tianKeDiChong(w.ganzhi, l.ganzhi)) {
3929
4228
  signals.push({ type: '运年反吟', target: `第${w.step}步大运`, detail: `大运${w.ganzhi}与流年${l.ganzhi}天克地冲` });
3930
4229
  }
4230
+ for (const key of ['year', 'month', 'day', 'hour']) {
4231
+ const g = pillars[key].ganzhi;
4232
+ if (!g) continue;
4233
+ if (w.ganzhi === g) {
4234
+ signals.push({ type: '大运伏吟', target: `第${w.step}步大运·${NATAL_LABEL[key]}`, detail: `大运${w.ganzhi}与${NATAL_LABEL[key]}同干支` });
4235
+ } else if (tianKeDiChong(w.ganzhi, g)) {
4236
+ signals.push({ type: '大运反吟', target: `第${w.step}步大运·${NATAL_LABEL[key]}`, detail: `大运${w.ganzhi}与${NATAL_LABEL[key]}${g}天克地冲` });
4237
+ }
4238
+ }
3931
4239
  }
3932
4240
  for (const key of ['year', 'month', 'day', 'hour']) {
3933
4241
  const g = pillars[key].ganzhi;
@@ -3941,7 +4249,7 @@ function buildBazi(input) {
3941
4249
  l.suiyun = {
3942
4250
  dayun: active.map((w) => ({ step: w.step, ganzhi: w.ganzhi })),
3943
4251
  signals,
3944
- note: '只标干支重复与天克地冲这类变动信号,不判吉凶;大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列',
4252
+ note: '只标干支重复与天克地冲这类变动信号,不判吉凶;大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列;大运与本命四柱的伏吟/反吟整步大运都在,逐年重复列出',
3945
4253
  };
3946
4254
  }
3947
4255
 
@@ -11225,6 +11533,9 @@ function buildPlain(r, ask, ctx = {}) {
11225
11533
  const ys = (r.frame && r.frame.yongshen) || {};
11226
11534
  const favor = join(ys.favor);
11227
11535
  const avoid = join(ys.avoid);
11536
+ const dis = Array.isArray(ys.disputed) && ys.disputed.length
11537
+ ? `有一点要说明:${ys.disputed.map((d) => d.element).join('、')}在扶抑里算${ys.disputed.map((d) => d.favorRole).join('、')}(能${ys.disputed.map((d) => d.favorVerb).join('、')}),按调候看又偏忌(${ys.disputed.map((d) => `${d.climate}盘忌${d.element}`).join('、')}),两派说法不一;实际用在什么时候,看季节和环境取舍,不必当成绝对的好或坏。`
11538
+ : '';
11228
11539
  const strength = STRENGTH_PLAIN[st.level] || '力量偏中性';
11229
11540
  const tiaohou = ys.tiaohou && ys.tiaohou.note ? `另外,${ys.tiaohou.note},所以「暖一点、亮一点」的环境通常更舒服。` : '';
11230
11541
  const sy = (r.liunian || []).find((l) => l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
@@ -11233,7 +11544,7 @@ function buildPlain(r, ask, ctx = {}) {
11233
11544
  : '';
11234
11545
  return `你的命局属于「${s(st.level, '中性')}」:${strength}。` +
11235
11546
  `${favor ? `能补${favor}的环境(行业、方位、作息、相处的人)会让你省力;${avoid ? `而${avoid}偏重的场合容易空耗,能绕就绕。` : ''}` : ''}` +
11236
- tiaohou + syPlain;
11547
+ dis + tiaohou + syPlain;
11237
11548
  }
11238
11549
  if (r.system === 'ziwei') {
11239
11550
  const body = r.body || {};
@@ -11368,10 +11679,10 @@ const KNOWLEDGE = [
11368
11679
  id: 'bazi-yueling-siquan',
11369
11680
  system: 'bazi', dims: ['overall', 'career'],
11370
11681
  gist: '月令不是「一个月」,是当月最有权的那一个字。',
11371
- detail: '月令藏干按本气、中气、余气分日用事,本气当权的时间最长。所以判旺衰先看日主在月令是得令还是失令——得令者未看全局,已有三分底气。本插件按 30 日表算出「生在本月第几日、哪一干当权」,给的是月内轮值这一层细节;旺衰与取格仍以月令本气、透干与会支为主,不随轮值改判。',
11372
- source: '《子平真诠》论用神;人元司权分日之说见《渊海子平》',
11682
+ detail: '月令藏干按本气、中气、余气分日用事,本气当权的时间最长。所以判旺衰先看日主在月令是得令还是失令——得令者未看全局,已有三分底气。本插件按 30 日表算出「生在本月第几日、哪一干当权」,给的是月内轮值这一层细节;旺衰与取格仍以月令本气、透干与会支为主,不随轮值改判。三命通会卷二·论人元司事另列一套日数(寅月五日/五日/二十日),与本插件取的通行表不同;原文并列,不改判。',
11683
+ source: '《子平真诠》论用神;人元司权分日之说见《渊海子平》;三命通会卷二·论人元司事(典藏网 7529)另列一套日数',
11373
11684
  status: 'variant',
11374
- note: '各家对中气、余气各主几日分法不同(有 7/7/16、9/3/18 等多说);本插件固定通行 30 日表(如寅月戊7丙7甲16),节当日为第 1 日,第 31 日以后按本气算、不硬分摊。司权表与本柱藏干表口径不同(子月壬、卯月甲、午月丙、酉月庚、亥月戊只出现在司权表里),两表各照原样;取格不随轮值改判。',
11685
+ note: '各家对中气、余气各主几日分法不同(有 7/7/16、9/3/18 等多说);本插件固定通行 30 日表(如寅月戊7丙7甲16),节当日为第 1 日,第 31 日以后按本气算、不硬分摊。三命通会此本寅月作 5/5/20、卯月作 7/23,与通行表不同,两表各照原样。司权表与本柱藏干表口径不同(子月壬、卯月甲、午月丙、酉月庚、亥月戊只出现在司权表里),两表各照原样;取格不随轮值改判。',
11375
11686
  },
11376
11687
  {
11377
11688
  id: 'bazi-tonggen-tougan',
@@ -11419,10 +11730,10 @@ const KNOWLEDGE = [
11419
11730
  id: 'bazi-suiyun',
11420
11731
  system: 'bazi', dims: ['overall', 'career'],
11421
11732
  gist: '大运管十年基调,流年管当年触发。',
11422
- detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(当值大运与流年干支相同)、运年反吟(大运与流年天克地冲)、流年伏吟(流年与原局某柱干支相同)与流年反吟(流年与原局某柱天克地冲),是传统上标记「变动较大」的年份,属于提示而非断语。本插件逐年标出四类信号:当值大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列;只标干支重复与对冲,不折分数。原文里「返吟」指岁运与日柱相对、「伏吟」指岁运压日,与本插件「干支相同为伏吟、天克地冲为反吟」不是同一套定义,两者并列、不合并。',
11733
+ detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(当值大运与流年干支相同)、运年反吟(大运与流年天克地冲)、大运伏吟(大运与原局某柱干支相同)、大运反吟(大运与原局某柱天克地冲)、流年伏吟(流年与原局某柱干支相同)、流年反吟(流年与原局某柱天克地冲),是传统上标记「变动较大」的年份,属于提示而非断语。本插件逐年标出六类信号:当值大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列;大运与本命四柱的伏吟/反吟整步大运都在,会连续多年重复列出;只标干支重复与对冲,不折分数。原文里「返吟」指岁运与日柱相对、「伏吟」指岁运压日,与本插件「干支相同为伏吟、天克地冲为反吟」不是同一套定义,两者并列、不合并。',
11423
11734
  source: '《三命通会》卷二·总论岁运(https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7539.html);岁运并临之说出自《渊海子平》',
11424
11735
  status: 'variant',
11425
- note: '「岁运并临主大凶」是流传最广也最被滥用的说法,本插件只作变动提示,不断吉凶;大运与原局之间的伏吟反吟未逐年标注,四柱十神层面的引化也未叠入。',
11736
+ note: '「岁运并临主大凶」是流传最广也最被滥用的说法,本插件只作变动提示,不断吉凶;大运与本命四柱的伏吟/反吟会连续多年出现,不逐年加重;四柱十神层面的引化也未叠入。',
11426
11737
  },
11427
11738
  {
11428
11739
  id: 'bazi-liuqin',
@@ -12350,6 +12661,7 @@ function baziInterpret(r) {
12350
12661
  const level = pick(st, 'level', '中和');
12351
12662
  const favor = Array.isArray(ys.favor) ? ys.favor : [];
12352
12663
  const avoid = Array.isArray(ys.avoid) ? ys.avoid : [];
12664
+ const disputed = Array.isArray(ys.disputed) ? ys.disputed : [];
12353
12665
  const primary = pick(frame, 'primaryName', '未取定');
12354
12666
  const gender = pick(r.input, 'gender', '');
12355
12667
  const isMale = gender === 'male';
@@ -12564,11 +12876,13 @@ function baziInterpret(r) {
12564
12876
  const nextSeg = ry.next ? `,第 ${ry.next.from} 日起交 ${ry.next.stem}${ry.next.element}` : '';
12565
12877
  const outside = ry.inHidden ? '' : ` 表里的 ${ry.active} 不在本柱藏干表中(属余气口径),两表各照原样,不互相改写。`;
12566
12878
  const beyond = ry.beyond ? ' 你生在第 31 日以后,已超出 30 日表,按本气算,不硬分摊。' : '';
12879
+ const ryc = r.renyuanClassic || classicOf('smt-renyuan');
12880
+ const src = ryc ? `\n原文:《${CLASSIC_BOOKS[ryc.book].name}·${ryc.chapter}》「${ryc.quote}」\n白话:${ryc.plain}` : '';
12567
12881
  return {
12568
12882
  key: 'renyuan', label: '人元司权',
12569
12883
  text: `生于${ry.month}月第 ${ry.day} 日(从${ry.term ? ry.term.name : '节'}起算,节当日为第 1 日,日差按本盘所用时刻所在日计):这一日由 **${ry.active}${ry.activeElement}** 司权,第 ${ry.segment.from}–${ry.segment.to} 日属它${nextSeg}。\n`
12570
12884
  + '月令这一个月里,余气、中气、本气轮值,不是整月同一个五行当值——所以「月令是什么」,还要看生在第几日。\n'
12571
- + `**口径**:本插件固定 30 日表(${table});另有 7/7/16、9/3/18 等分法,各家不一。分日只作解释层的一层细节,**不改取格**:取格仍按本气、透干与会支判定。${outside}${beyond}`,
12885
+ + `**口径**:本插件固定通行 30 日表(${table});另有 7/7/16、9/3/18 等分法,三命通会此本又作 5/5/20(见下原文),各家不一。分日只作解释层的一层细节,**不改取格**:取格仍按本气、透干与会支判定。${outside}${beyond}` + src,
12572
12886
  };
12573
12887
  })()] : []),
12574
12888
  {
@@ -12600,8 +12914,8 @@ function baziInterpret(r) {
12600
12914
  const stem = (l.suiyun.dayun || []).map((d) => `第${d.step}步${d.ganzhi}`).join('、');
12601
12915
  return `${l.year} ${l.ganzhi}(${stem || '未到起运'}):${l.suiyun.signals.map((s) => s.detail).join(';')}`;
12602
12916
  }).join('\n') +
12603
- '\n读法:岁运并临、运年反吟与流年伏吟/反吟只说明干支层面重复或对冲,传统上当作「变动感较强」的信号;本插件不据此判吉凶,要落到具体年份,仍看该年是否动到用神。' + src
12604
- : '近十年没有岁运并临、运年反吟或流年伏吟/反吟。「岁运并临主大凶」这类说法流传很广,本插件只把它当变动提示,没有命中就不作额外解读。',
12917
+ '\n读法:岁运并临、运年反吟、大运与流年的伏吟/反吟只说明干支层面重复或对冲,传统上当作「变动感较强」的信号;本插件不据此判吉凶,要落到具体年份,仍看该年是否动到用神。大运与本命四柱的伏吟/反吟整步大运都在,会连续多年出现,不当成逐年加重。' + src
12918
+ : '近十年没有岁运并临、运年反吟或大运/流年伏吟、反吟。「岁运并临主大凶」这类说法流传很广,本插件只把它当变动提示,没有命中就不作额外解读。',
12605
12919
  };
12606
12920
  })(),
12607
12921
  {
@@ -12637,7 +12951,7 @@ function baziInterpret(r) {
12637
12951
  ...(cli ? [{
12638
12952
  key: 'climate', label: '寒暖燥湿',
12639
12953
  text: `${cli.narration}${cli.need
12640
- ? `调候取${cli.need.stem}${cli.need.element},忌${cli.avoidElement};${cli.clash ? '与扶抑冲突,本盘以调候为先' : '与扶抑同向'}`
12954
+ ? `调候取${cli.need.stem}${cli.need.element},忌${cli.avoidElement}${disputed.some((d) => d.element === cli.avoidElement) ? '(有争议:该行在扶抑中为喜,见用神节)' : ''};${cli.clash ? '与扶抑冲突,本盘以调候为先' : disputed.some((d) => d.element === cli.avoidElement) ? '调候所忌在扶抑里为喜,两说并存' : '与扶抑同向'}`
12641
12955
  : '气候平和,调候不作主取'}。`,
12642
12956
  }] : []),
12643
12957
  ...(ys.qt ? [{
@@ -13788,7 +14102,7 @@ const BUILDERS = {
13788
14102
  const { interpretQimen } = __mods['qimen-interpret'];
13789
14103
  const { buildAnswer, buildPlain, buildGlossary, buildFollowups, dimensionPlain } = __mods['interpret-plain'];
13790
14104
  const { knowledgeFor, caveatsFor, STATUS_LABEL } = __mods['interpret-knowledge'];
13791
- const { CLASSIC_BOOKS, suiyunClassicOf } = __mods.classics;
14105
+ const { CLASSIC_BOOKS, suiyunClassicOf, classicOf } = __mods.classics;
13792
14106
 
13793
14107
  function enrichPlain(result, r, ask) {
13794
14108
  if (!result) return result;
@@ -14494,7 +14808,8 @@ const { buildStar } = __mods.star;
14494
14808
  const { buildNumerology } = __mods.numerology;
14495
14809
  const { buildMbti, mbtiPaper } = __mods.mbti;
14496
14810
  const { buildConvert } = __mods.convert;
14497
- const { classicsFor, classicsText, suiyunClassicOf, CLASSIC_BOOKS } = __mods.classics;
14811
+ const { classicsFor, classicsText, suiyunClassicOf, classicOf, CLASSIC_BOOKS } = __mods.classics;
14812
+ const { diffObjects, summarize, sortDiffs, labelOf, rootOf, categoryOf, formatValue, CALIBER_SWITCHES } = __mods.diff;
14498
14813
  const { buildTrend } = __mods.trend;
14499
14814
  const { buildFengshui } = __mods.fengshui;
14500
14815
  const { buildName } = __mods.name;
@@ -14585,15 +14900,21 @@ function baziText(r) {
14585
14900
  `${g.cat}|` + g.items.map((i) => `${i.name}(${i.pillars.map((k) => SHORT[k]).join('')})`).join('、')).join(';')
14586
14901
  : '神煞:—';
14587
14902
  const ry = r.renyuan;
14903
+ const rySrc = classicOf('smt-renyuan');
14588
14904
  const renyuanLine = ry
14589
- ? `人元:${ry.month}月第${ry.day}日,${ry.active}${ry.activeElement}司权(${ry.segments.map((s) => `${s.stem}${s.from}–${s.to}`).join(' ')})${ry.beyond ? ';已过 30 日表,按本气算' : ''}`
14905
+ ? `人元:${ry.month}月第${ry.day}日,${ry.active}${ry.activeElement}司权(${ry.segments.map((s) => `${s.stem}${s.from}–${s.to}`).join(' ')})${ry.beyond ? ';已过 30 日表,按本气算' : ''}${rySrc ? `|原文见《${CLASSIC_BOOKS[rySrc.book].name}·${rySrc.chapter}》` : ''}`
14906
+ : '';
14907
+ const ysd = (r.frame && r.frame.yongshen && r.frame.yongshen.disputed) || [];
14908
+ const disText = ysd.length
14909
+ ? ` 有争议:${ysd.map((d) => `${d.element}(${d.favorRole};${d.climate}盘调候忌${d.element})`).join('、')}`
14590
14910
  : '';
14911
+ const disEls = new Set(ysd.map((d) => d.element));
14591
14912
  const gejuLine = r.frame ? [
14592
14913
  `格局:${r.frame.primary ? `${r.frame.primary.name}(${r.frame.primary.evidence})` : '—'}`,
14593
14914
  `   ${r.frame.geju.map((g) => `${g.name}[${g.kind}]`).join(' ')}`,
14594
- `用神:喜${r.frame.yongshen.favor.join('')}(${r.frame.yongshen.favorTenGods.join('、')}) 忌${r.frame.yongshen.avoid.join('')}(${r.frame.yongshen.avoidTenGods.join('、')}) ${r.frame.yongshen.method}`,
14915
+ `用神:喜${r.frame.yongshen.favor.join('')}(${r.frame.yongshen.favorTenGods.join('、')}) 忌${r.frame.yongshen.avoid.join('')}(${r.frame.yongshen.avoidTenGods.join('、')})${disText} ${r.frame.yongshen.method}`,
14595
14916
  `   ${r.frame.yongshen.detail}`,
14596
- `寒暖:${r.climate ? `${r.climate.label}(${(r.climate.evidence || []).join('、')})${r.climate.need ? ` 调候取${r.climate.need.stem}${r.climate.need.element} 忌${r.climate.avoidElement}` : ''}` : '—'}` +
14917
+ `寒暖:${r.climate ? `${r.climate.label}(${(r.climate.evidence || []).join('、')})${r.climate.need ? ` 调候取${r.climate.need.stem}${r.climate.need.element} 忌${r.climate.avoidElement}${disEls.has(r.climate.avoidElement) ? '(有争议)' : ''}` : ''}` : '—'}` +
14597
14918
  (r.frame.yongshen.qt ? ` 穷通:${r.frame.yongshen.qt.month}${r.frame.yongshen.qt.stem}取${r.frame.yongshen.qt.yong.join('、')}(首取${r.frame.yongshen.qt.primary})` : ''),
14598
14919
  `十神权重:${Object.entries(r.frame.godStats.groups).map(([k, v]) => `${k}${v}`).join(' ')}`,
14599
14920
  ] : [];
@@ -14616,8 +14937,8 @@ function baziText(r) {
14616
14937
  `流年:${r.liunian.slice(0, 10).map((l) => `${l.year}${l.ganzhi}(${l.stemTenGod})${l.luck ? `·${l.luck.tier}` : ''}`).join(' ')}`,
14617
14938
  (() => {
14618
14939
  const hits = r.liunian.filter((l) => l.suiyun && l.suiyun.signals && l.suiyun.signals.length);
14619
- if (!hits.length) return '岁运提示:近十年无岁运并临、运年反吟或流年伏吟/反吟(只标干支,不判吉凶)';
14620
- const label = (s) => `${s.type}${s.type.startsWith('流年') && s.target ? `(${s.target})` : ''}`;
14940
+ if (!hits.length) return '岁运提示:近十年无岁运并临、运年反吟或大运/流年伏吟、反吟(只标干支,不判吉凶)';
14941
+ const label = (s) => `${s.type}${(s.type.startsWith('流年') || s.type.startsWith('大运')) && s.target ? `(${s.target})` : ''}`;
14621
14942
  const e = suiyunClassicOf(r);
14622
14943
  const src = e ? `|古籍见《${CLASSIC_BOOKS[e.book].name}·${e.chapter}》` : '';
14623
14944
  return '岁运提示(只标干支变动,不判吉凶):' + hits
@@ -14882,6 +15203,10 @@ function compute(args = {}) {
14882
15203
  if (ct) r.summary = `${r.summary}\n【古籍】\n${ct}`;
14883
15204
  const syc = suiyunClassicOf(r);
14884
15205
  if (syc) r.suiyunClassic = { ...syc, bookName: CLASSIC_BOOKS[syc.book].name };
15206
+ if (r.renyuan) {
15207
+ const ryc = classicOf('smt-renyuan');
15208
+ if (ryc) r.renyuanClassic = { ...ryc, bookName: CLASSIC_BOOKS[ryc.book].name };
15209
+ }
14885
15210
  }
14886
15211
  if (r && r.precision && r.precision.level === 'approx' && r.summary) {
14887
15212
  r.summary = `⚠ ${r.precision.note}\n${r.summary}`;
@@ -14988,6 +15313,64 @@ function interpretText(i) {
14988
15313
  return out.join('\n');
14989
15314
  }
14990
15315
 
15316
+ const CALIBER_IGNORE_ARGS = ['cross', 'verifyPast', 'withInterpret', 'question', 'direction', 'ask', 'geocode', 'rectify', 'events'];
15317
+
15318
+ function caliberCompare(args = {}, opts = {}) {
15319
+ const system = opts.system || args.system;
15320
+ const switches = opts.switches || CALIBER_SWITCHES[system] || [];
15321
+ if (!switches.length) return { system, switches: [], affected: 0, total: 0 };
15322
+ const base = { ...args };
15323
+ for (const k of CALIBER_IGNORE_ARGS) delete base[k];
15324
+ const rows = switches.map((sw) => {
15325
+ const current = sw.read(base);
15326
+ const alt = sw.alt(current);
15327
+ let diffs = [];
15328
+ let error = '';
15329
+ try {
15330
+ diffs = diffObjects(
15331
+ compute({ ...base, [sw.key]: current }),
15332
+ compute({ ...base, [sw.key]: alt }),
15333
+ );
15334
+ } catch (e) {
15335
+ error = (e && e.message) || String(e);
15336
+ }
15337
+ const roots = sortDiffs(diffs.filter((d) => rootOf(d.path))).map((d) => ({
15338
+ path: d.path,
15339
+ label: labelOf(d.path),
15340
+ from: formatValue(d.from),
15341
+ to: formatValue(d.to),
15342
+ }));
15343
+ const counts = new Map();
15344
+ for (const d of diffs) {
15345
+ const c = categoryOf(d.path) || '其他';
15346
+ counts.set(c, (counts.get(c) || 0) + 1);
15347
+ }
15348
+ const categories = [...counts.entries()]
15349
+ .map(([name, count]) => ({ name, count }))
15350
+ .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name, 'zh-Hans-CN'));
15351
+ return {
15352
+ key: sw.key,
15353
+ title: sw.title,
15354
+ short: sw.short,
15355
+ current,
15356
+ alt,
15357
+ currentLabel: sw.label(current),
15358
+ altLabel: sw.label(alt),
15359
+ total: diffs.length,
15360
+ summary: error ? '' : summarize(diffs),
15361
+ roots,
15362
+ categories,
15363
+ error,
15364
+ };
15365
+ });
15366
+ return {
15367
+ system,
15368
+ switches: rows,
15369
+ affected: rows.filter((r) => !r.error && r.total > 0).length,
15370
+ total: rows.reduce((n, r) => n + r.total, 0),
15371
+ };
15372
+ }
15373
+
14991
15374
  const JU_METHODS = ['chaibu', 'chaoshen', 'zhirun'];
14992
15375
  const TAROT_COUNTS = [1, 3, 5];
14993
15376
  const LATE_ZI_MODES = ['same-day', 'next-day'];
@@ -15582,6 +15965,15 @@ function renderText(result) {
15582
15965
  if (extra) parts.push(extra);
15583
15966
  }
15584
15967
  if (result.cross) parts.push(`【八字 × 紫微 交叉验证】\n${crossText(result.cross)}`);
15968
+ if (result.caliber && Array.isArray(result.caliber.switches) && result.caliber.switches.length) {
15969
+ const c = result.caliber;
15970
+ parts.push('【口径对照】同一组输入,只换一个口径(不判哪套更准,也不改盘面)\n'
15971
+ + c.switches.map((s) => {
15972
+ if (s.error) return `· ${s.title}:未能对照(${s.error})`;
15973
+ if (!s.total) return `· ${s.title}:${s.currentLabel} → ${s.altLabel},本盘无变化`;
15974
+ return `· ${s.title}:${s.currentLabel} → ${s.altLabel}\n ${s.summary}`;
15975
+ }).join('\n'));
15976
+ }
15585
15977
  if (result.followup) {
15586
15978
  const f = result.followup;
15587
15979
  parts.push(`【追问】${f.question}\n${f.answer}${f.evidence && f.evidence.length ? `\n 依据:${f.evidence.join(';')} 置信度:${f.confidence}` : ''}`);
@@ -15600,12 +15992,12 @@ function renderText(result) {
15600
15992
  return parts.filter(Boolean).join('\n\n');
15601
15993
  }
15602
15994
 
15603
- 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 };
15995
+ 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, caliberCompare, renderText };
15604
15996
  })();
15605
15997
 
15606
15998
  const Engine = __mods.index;
15607
15999
  window.Engine = Engine;
15608
- window.__MP_VER = '0.50.1';
16000
+ window.__MP_VER = '0.50.5';
15609
16001
  window.__MPGroups = [{"title":"问一件事","hint":"心里有一件具体的事,起一卦再决定","items":[{"k":"liuyao","label":"六爻","tip":"摇六次,问一件事"},{"k":"meihua","label":"梅花","tip":"见闻起卦,看当下"},{"k":"tarot","label":"塔罗","tip":"抽牌,看关系与选择"},{"k":"qimen","label":"奇门","tip":"看方位、时机与取舍"},{"k":"daliuren","label":"六壬","tip":"占事,断来意与结果"},{"k":"xiaoliuren","label":"小六壬","tip":"掐指一算,快速断"}]},{"title":"看一个人","hint":"看自己或身边人的性情、格局与运程","items":[{"k":"bazi","label":"八字","tip":"四柱看一生格局"},{"k":"ziwei","label":"紫微","tip":"十二宫细看"},{"k":"hepan","label":"合盘","tip":"两个人合不合"},{"k":"mbti","label":"人格","tip":"16 型性格问卷"},{"k":"chenggu","label":"称骨","tip":"按生辰称命重"},{"k":"numerology","label":"数字","tip":"生日数字看性格"},{"k":"trend","label":"人生K","tip":"一生高低走势","wide":true}]},{"title":"查日子","hint":"挑时间、换农历,顺带看看家里和名字","items":[{"k":"almanac","label":"黄历","tip":"今天宜忌、时辰吉凶"},{"k":"zeri","label":"择日","tip":"挑个办事的好日子"},{"k":"convert","label":"农历","tip":"公历农历互转"},{"k":"star","label":"星座","tip":"星座性格与今日运"},{"k":"fengshui","label":"风水","tip":"房屋朝向与布置"},{"k":"name","label":"起名","tip":"按八字选字"}]}];
15610
16002
  (function () {
15611
16003
  const NEED_ASK = { liuyao: true, meihua: true, daliuren: true, tarot: true };
@@ -15637,7 +16029,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
15637
16029
 
15638
16030
  (function () {
15639
16031
  const CSS = `
15640
- .pp,.mp-it,.mp-al,.mp-cg,.mp-cl,.mp-cli,.mp-kline,.mp-mb,.mp-mb-paper,.mp-nm,.mp-quick{--ink:#1b1b1b;--ink2:#3f3a33;--muted:#6f6a60;--label:#8a8378;--faint:#a9a296;
16032
+ .pp,.mp-it,.mp-al,.mp-cg,.mp-cl,.mp-cli,.mp-kline,.mp-mb,.mp-mb-paper,.mp-nm,.mp-quick,.mp-cdiff{--ink:#1b1b1b;--ink2:#3f3a33;--muted:#6f6a60;--label:#8a8378;--faint:#a9a296;
15641
16033
  --line:#c8c1b2;--dimline:#b9b2a4;--hair:#e9e2d3;
15642
16034
  --bg:#f6f1e6;--card:#fffdf8;--band:#fbf7ee;--hover:#f3ede1;--hover2:#ece5d6;
15643
16035
  --tri:#f3e8cd;--soul:#fae4dc;--sel:#fceee7;--link:#f9ece0;
@@ -15650,7 +16042,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
15650
16042
  .pp{border:1px solid var(--line);background:var(--bg);color:var(--ink);font-family:"Songti SC","SimSun","Microsoft YaHei",serif;font-size:12.5px;line-height:1.55;
15651
16043
  border-radius:12px;overflow:hidden;box-shadow:var(--shadow);container-type:inline-size}
15652
16044
  @media screen and (prefers-color-scheme:dark){
15653
- .pp,.mp-it,.mp-al,.mp-cg,.mp-cl,.mp-cli,.mp-kline,.mp-mb,.mp-mb-paper,.mp-nm,.mp-quick{--ink:#eae5d9;--ink2:#d5cfc2;--muted:#aca598;--label:#a49c8e;--faint:#928b7e;
16045
+ .pp,.mp-it,.mp-al,.mp-cg,.mp-cl,.mp-cli,.mp-kline,.mp-mb,.mp-mb-paper,.mp-nm,.mp-quick,.mp-cdiff{--ink:#eae5d9;--ink2:#d5cfc2;--muted:#aca598;--label:#a49c8e;--faint:#928b7e;
15654
16046
  --line:#514a3e;--dimline:#5f5849;--hair:#2f2b23;
15655
16047
  --bg:#12110c;--card:#211f18;--band:#282419;--hover:#2e2a1f;--hover2:#383322;
15656
16048
  --tri:#332913;--soul:#3a231c;--sel:#43271f;--link:#33241c;
@@ -16112,6 +16504,49 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
16112
16504
  .mp-cross-cell{font-size:12.5px;line-height:1.75;color:var(--ink2)}
16113
16505
  .mp-cross-cell b{display:block;font-size:10.5px;letter-spacing:.1em;color:var(--muted);font-weight:600;margin-bottom:1px}
16114
16506
  .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)}
16507
+ .mp-cdiff{margin:12px 0 0;border:1px solid var(--line);border-radius:10px;background:var(--card);overflow:hidden;font-family:system-ui,sans-serif}
16508
+ .mp-cdiff>summary{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-height:44px;padding:8px 13px;cursor:pointer;list-style:none;color:var(--ink2);font-size:12.5px;line-height:1.6}
16509
+ .mp-cdiff>summary::-webkit-details-marker{display:none}
16510
+ .mp-cdiff>summary::before{content:'▸';color:var(--red);font-size:12px}
16511
+ .mp-cdiff[open]>summary::before{content:'▾'}
16512
+ .mp-cdiff-title{font-size:13px;font-weight:700;letter-spacing:.06em;color:var(--red)}
16513
+ .mp-cdiff-lead{color:var(--muted)}
16514
+ .mp-cdiff-more{margin-left:auto;color:var(--label);font-size:11.5px;white-space:nowrap}
16515
+ .mp-cdiff[open] .mp-cdiff-more{display:none}
16516
+ .mp-cdiff-body{padding:0 13px 12px;border-top:1px dashed var(--line)}
16517
+ .mp-cdiff-row{padding:9px 0;border-bottom:1px dashed var(--line)}
16518
+ .mp-cdiff-row:last-of-type{border-bottom:none}
16519
+ .mp-cdiff-h{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
16520
+ .mp-cdiff-h b{font-size:12.5px;color:var(--ink);font-weight:700}
16521
+ .mp-cdiff-count{margin-left:auto;font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums}
16522
+ .mp-cdiff-count.on{color:var(--red)}
16523
+ .mp-cdiff-ab{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:5px;font-size:11.5px;color:var(--muted)}
16524
+ .mp-cdiff-pill{border:1px solid var(--line);border-radius:999px;padding:1px 8px;background:var(--band)}
16525
+ .mp-cdiff-pill.now{color:var(--lu);border-color:rgba(var(--lu-rgb),.4);background:color-mix(in srgb,var(--lu) 10%,transparent)}
16526
+ .mp-cdiff-pill.alt{color:var(--red);border-color:rgba(var(--red-rgb),.4);background:color-mix(in srgb,var(--red) 8%,transparent)}
16527
+ .mp-cdiff-arrow{color:var(--dimline)}
16528
+ .mp-cdiff-roots{display:flex;flex-direction:column;gap:3px;margin-top:6px;font-size:12.5px;line-height:1.7}
16529
+ .mp-cdiff-root{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap}
16530
+ .mp-cdiff-k{color:var(--label);font-size:11px;letter-spacing:.08em;min-width:2.2em}
16531
+ .mp-cdiff-from{color:var(--ink2)}
16532
+ .mp-cdiff-to{color:var(--ink);font-weight:700}
16533
+ .mp-cdiff-cats{display:flex;flex-wrap:wrap;gap:4px 6px;margin-top:6px}
16534
+ .mp-cdiff-cat{font-size:11px;color:var(--muted);border:1px solid var(--line);border-radius:999px;padding:0 7px;background:var(--band)}
16535
+ .mp-cdiff-same{font-size:12px;color:var(--muted);margin-top:4px}
16536
+ .mp-cdiff-note{margin-top:8px;padding-top:7px;border-top:1px dashed var(--line);font-size:11.5px;line-height:1.7;color:var(--muted)}
16537
+ @media (max-width:640px){
16538
+ .mp-cdiff>summary{padding:8px 11px}
16539
+ .mp-cdiff-more{display:none}
16540
+ .mp-cdiff-body{padding:0 11px 10px}
16541
+ .mp-cdiff-h b{font-size:12px}
16542
+ .mp-cdiff-count{margin-left:0}
16543
+ }
16544
+ @media print{
16545
+ .mp-cdiff:not([open])>*{display:block!important}
16546
+ .mp-cdiff>summary{cursor:auto}
16547
+ .mp-cdiff-more{display:none}
16548
+ .mp-cdiff{break-inside:avoid}
16549
+ }
16115
16550
  .mp-tables{border-top:1px solid var(--line);padding:12px 16px 4px;background:var(--card)}
16116
16551
  .mp-table-wrap{margin-bottom:12px}
16117
16552
  .mp-table-h{font-size:12px;font-weight:700;letter-spacing:.1em;color:var(--lu);margin-bottom:5px;font-family:system-ui,sans-serif}
@@ -17694,12 +18129,16 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
17694
18129
  const ys = fr.yongshen ? `
17695
18130
  <div class="pp-tl">喜 ${fr.yongshen.favor.map(el).join('')} 忌 ${fr.yongshen.avoid.map(el).join('')} <span class="pp-dim">${esc(fr.yongshen.method)}</span></div>
17696
18131
  <div class="pp-chips">${fr.yongshen.favorTenGods.map((t) => `<span class="pp-chip ok">${esc(t)}</span>`).join('')}${fr.yongshen.avoidTenGods ? fr.yongshen.avoidTenGods.map((t) => `<span class="pp-chip warn">${esc(t)}</span>`).join('') : ''}</div>
18132
+ ${(fr.yongshen.disputed || []).length
18133
+ ? `<div class="pp-tl pp-dim">有争议:${fr.yongshen.disputed.map((d) => `${esc(d.element)}(扶抑为${esc(d.favorRole)}·${esc(d.favorVerb)},${esc(d.climate)}盘调候忌${esc(d.element)})`).join('、')} 保留在喜用,不列入忌</div>`
18134
+ : ''}
17697
18135
  <div class="pp-tl pp-dim">${esc(fr.yongshen.detail)}</div>` : '';
17698
18136
  const cli = r.climate || (fr.yongshen && fr.yongshen.climate) || null;
17699
18137
  const cliCard = cli ? (() => {
17700
18138
  const pct = Math.max(2, Math.min(98, Math.round(((Number(cli.temp) || 0) + 3) / 6 * 100)));
17701
18139
  const need = cli.need ? `<em>调候取</em><b>${esc(cli.need.stem + cli.need.element)}</b>` : '<em>气候平和</em>';
17702
- const avoid = cli.need ? `<em>忌</em><b>${esc(cli.avoidElement)}</b>` : '';
18140
+ const cliDis = !!(fr.yongshen && Array.isArray(fr.yongshen.disputed)) && fr.yongshen.disputed.some((d) => d.element === cli.avoidElement);
18141
+ const avoid = cli.need ? `<em>忌</em><b>${esc(cli.avoidElement)}</b>${cliDis ? '<span class="pp-dim">(有争议)</span>' : ''}` : '';
17703
18142
  const ev = (cli.evidence || []).length ? `<em>依据</em><span>${esc(cli.evidence.join('、'))}</span>` : '';
17704
18143
  const pri = cli.priority === '调候优先' ? '<span class="mp-cli-p">调候优先</span>' : '<span class="mp-cli-p">扶抑为主</span>';
17705
18144
  const qt = r.frame && r.frame.yongshen && r.frame.yongshen.qt;
@@ -17733,7 +18172,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
17733
18172
  const syc = r.suiyunClassic;
17734
18173
  const sySrc = syc ? `<div class="pp-dim pp-sy-src" title="${esc(syc.plain || '')}">《${esc(syc.bookName || '')}·${esc(syc.chapter || '')}》「${esc(syc.quote || '')}」</div>` : '';
17735
18174
  const syRow = sy.length ? `<div class="pp-tl pp-dim">岁运提示(只标干支变动,不判吉凶)</div><div class="pp-chips">${sy.map((l) =>
17736
- `<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}` : '';
18175
+ `<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.type.startsWith('大运')) && x.target ? `·${x.target}` : ''}`).join('/'))}</span>`).join('')}</div>${sySrc}` : '';
17737
18176
  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>` : '';
17738
18177
  const ssGroups = (r.shensha && r.shensha.groups) || [];
17739
18178
  const ssx = ssGroups.length ? `
@@ -17878,6 +18317,43 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
17878
18317
  return (html || '') + pp.interpretHTML(it, opt);
17879
18318
  };
17880
18319
 
18320
+ pp.caliberHTML = function (caliber) {
18321
+ const sws = caliber && Array.isArray(caliber.switches) ? caliber.switches.filter((s) => s && s.key) : [];
18322
+ if (!sws.length) return '';
18323
+ const affected = sws.filter((s) => !s.error && s.total > 0);
18324
+ const lead = affected.length
18325
+ ? `${sws.length} 组开关中 ${affected.length} 组影响此盘:${affected.map((s) => s.short || s.title).join('、')}。`
18326
+ : `本盘对 ${sws.length} 组开关都不敏感:换成另一档也不变。`;
18327
+ const rootsHTML = (row) => {
18328
+ if (row.error) return `<div class="mp-cdiff-same">未能对照:${esc(row.error)}</div>`;
18329
+ if (!row.total) return '<div class="mp-cdiff-same">无变化 · 此盘不受这一开关影响</div>';
18330
+ const roots = Array.isArray(row.roots) ? row.roots : [];
18331
+ if (!roots.length) return `<div class="mp-cdiff-same">无单一根因;逐字段差异 ${Number(row.total) || 0} 处</div>`;
18332
+ return `<div class="mp-cdiff-roots">${roots.map((r) => `<div class="mp-cdiff-root">`
18333
+ + `<span class="mp-cdiff-k">${esc(r.label)}</span><span class="mp-cdiff-from">${esc(r.from)}</span>`
18334
+ + `<span class="mp-cdiff-arrow" aria-hidden="true">→</span><span class="mp-cdiff-to">${esc(r.to)}</span></div>`).join('')}</div>`;
18335
+ };
18336
+ const catsHTML = (row) => {
18337
+ const cats = Array.isArray(row.categories) ? row.categories : [];
18338
+ if (row.error || !row.total || !cats.length) return '';
18339
+ const shown = cats.slice(0, 6);
18340
+ return `<div class="mp-cdiff-cats">${shown.map((c) => `<span class="mp-cdiff-cat">${esc(c.name)} ${Number(c.count) || 0} 处</span>`).join('')}`
18341
+ + (cats.length > shown.length ? `<span class="mp-cdiff-cat">另 ${cats.length - shown.length} 类</span>` : '') + '</div>';
18342
+ };
18343
+ const rows = sws.map((row) => `<div class="mp-cdiff-row">`
18344
+ + `<div class="mp-cdiff-h"><b>${esc(row.title)}</b>`
18345
+ + `<span class="mp-cdiff-count${row.total > 0 ? ' on' : ''}">${row.error ? '未能对照' : row.total ? `${Number(row.total) || 0} 处` : '无变化'}</span></div>`
18346
+ + `<div class="mp-cdiff-ab"><span class="mp-cdiff-pill now">当前 · ${esc(row.currentLabel)}</span>`
18347
+ + `<span class="mp-cdiff-arrow" aria-hidden="true">→</span><span class="mp-cdiff-pill alt">换成 · ${esc(row.altLabel)}</span></div>`
18348
+ + `${rootsHTML(row)}${catsHTML(row)}</div>`).join('');
18349
+ return `<details class="mp-cdiff" aria-label="口径对照">
18350
+ <summary><span class="mp-cdiff-title">口径对照</span><span class="mp-cdiff-lead">${esc(lead)}</span><span class="mp-cdiff-more">展开逐字差异</span></summary>
18351
+ <div class="mp-cdiff-body">${rows}
18352
+ <div class="mp-cdiff-note">同一组输入、只换一个开关;只列变化字段与根因,不判哪套口径更准,也不改上方盘面。</div>
18353
+ </div>
18354
+ </details>`;
18355
+ };
18356
+
17881
18357
  pp.almanacHTML = function (r) {
17882
18358
  if (!r || !r.meta) return pp.quickHTML(r);
17883
18359
  const g = r.ganzhi || {};
@@ -18845,6 +19321,10 @@ function App() {
18845
19321
  });
18846
19322
  } catch (e) { trend = null; }
18847
19323
  }
19324
+ let caliber = null;
19325
+ if (Engine.caliberCompare) {
19326
+ try { caliber = Engine.caliberCompare(args); } catch (e) { caliber = null; }
19327
+ }
18848
19328
  const ask = (needAsk(k) || k === 'qimen')
18849
19329
  ? { question: needAsk(k) ? question.trim() : '', direction }
18850
19330
  : undefined;
@@ -18853,7 +19333,7 @@ function App() {
18853
19333
  it.sections = [...(it.sections || []), Engine.crossSection(r.cross)];
18854
19334
  }
18855
19335
  const timed = kindOf(k) === 'cast' && (k !== 'meihua' || mh === 'time');
18856
- setResult({ kind: k, r, it, at: dt, timed, trend });
19336
+ setResult({ kind: k, r, it, at: dt, timed, trend, caliber });
18857
19337
  setFx(null); setFit(null); setPastMarks({}); setRectRes(null);
18858
19338
  setError(''); setCopied('');
18859
19339
  if (k !== 'tarot' && k !== 'mbti') {
@@ -19437,7 +19917,7 @@ function App() {
19437
19917
  : PP.quickHTML(result.r),
19438
19918
  result.it,
19439
19919
  { folded: defaultFolded() },
19440
- ),
19920
+ ) + PP.caliberHTML(result.caliber),
19441
19921
  },
19442
19922
  }) : null,
19443
19923