dsh-metaphysics 0.34.0 → 0.38.2

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,5 +1,6 @@
1
1
  window.__ModuleLoader__.load({
2
2
  id: 'dsh-metaphysics',
3
+ version: '0.38.2',
3
4
  factory(require) {
4
5
  const React = (function () {
5
6
  try { return require('react'); } catch (e) { return globalThis.React; }
@@ -2514,6 +2515,7 @@ __mods.classics = (function () {
2514
2515
  const CLASSIC_BOOKS = {
2515
2516
  ziping: { name: '子平真诠', author: '清·沈孝瞻', base: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/' },
2516
2517
  ditian: { name: '滴天髓', author: '明·京图(通行本)', base: 'https://www.diancang.xyz/xuanxuewushu/ditiansui/' },
2518
+ sanming: { name: '三命通会', author: '明·万民英', base: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/' },
2517
2519
  };
2518
2520
 
2519
2521
  const CLASSICS = [
@@ -2617,6 +2619,26 @@ const CLASSICS = [
2617
2619
  plain: '月令用神为主,但必须配气候参看;同样的格局,生在得时的气候里事半功倍,逆着气候就费劲。',
2618
2620
  url: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/53634.html',
2619
2621
  },
2622
+ {
2623
+ id: 'zp-chengbai',
2624
+ book: 'ziping',
2625
+ chapter: '九、论用神成败救应',
2626
+ scene: '格局',
2627
+ tags: [],
2628
+ quote: '成中有败,必是带忌;败中有成,全凭救应。',
2629
+ plain: '成格不等于一路顺:成中带忌(官逢财又逢伤、官被合之类)就打了折;败格也未必无救——印来解伤、合来清煞,败中还能转成。',
2630
+ url: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/53629.html',
2631
+ },
2632
+ {
2633
+ id: 'zp-bianhua',
2634
+ book: 'ziping',
2635
+ chapter: '十、论用神变化',
2636
+ scene: '格局',
2637
+ tags: [],
2638
+ quote: '故若丁生亥月,本为正官,支全卯未,则化为印。己生申月,本属伤官。藏庚透壬,则化为财。凡此之类皆用神之变化也。',
2639
+ plain: '用神本从月令取,但月令藏干不一:丁生亥月本为正官,支会卯未就化为印;己生申月本属伤官,庚藏壬透就化为财。起点相同,合化一变,取用也跟着变(原文只说变化之由,不断定某盘必化)。',
2640
+ url: 'https://www.diancang.xyz/xuanxuewushu/zipingzhenquan/53630.html',
2641
+ },
2620
2642
  {
2621
2643
  id: 'dt-hannuan',
2622
2644
  book: 'ditian',
@@ -2707,6 +2729,76 @@ const CLASSICS = [
2707
2729
  plain: '看精神不能偏执一端,要在损益之间求其中。',
2708
2730
  url: 'https://www.diancang.xyz/xuanxuewushu/ditiansui/153091.html',
2709
2731
  },
2732
+ {
2733
+ id: 'smt-zhenguan',
2734
+ book: 'sanming',
2735
+ chapter: '卷五·论正官',
2736
+ scene: '格局',
2737
+ tags: ['正官'],
2738
+ quote: '正官者,乃甲见辛、乙见庚之例。阴阳配合,相制有用,成其道也。故正官为六格之首,止许一位,多则不宜。',
2739
+ plain: '正官取「克我而阴阳相配」,有制而能成用;六格以正官居首,只宜一位,多见就杂。',
2740
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7582.html',
2741
+ },
2742
+ {
2743
+ id: 'smt-zhengcai',
2744
+ book: 'sanming',
2745
+ chapter: '卷六·论正财',
2746
+ scene: '格局',
2747
+ tags: ['财'],
2748
+ quote: '正财者,乃甲见己、乙见戊之例。受我克制,为我之妻,譬人娶妻,妻赍财嫁我,我必精神康强,而后可享用,若衰微不振,虽妻财丰厚,但能目视,终不得用。',
2749
+ plain: '正财是我克制之物,古人以「娶妻带财」作比:自身精神康强才受用得起,身弱不振,财再厚也只是看着。',
2750
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7645.html',
2751
+ },
2752
+ {
2753
+ id: 'smt-yinshou',
2754
+ book: 'sanming',
2755
+ chapter: '卷六·论印绶',
2756
+ scene: '格局',
2757
+ tags: ['印'],
2758
+ quote: '印绶者,乃五行生我之名。如甲乙在亥子月,丙丁在寅卯月之类,乃我气之源,为生气,为父母,能护我官星,使无伤克',
2759
+ plain: '印是生我之气,像源头与父母:它能护住官星,使官不受伤克;反过来说,印重就怕财来破。',
2760
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7655.html',
2761
+ },
2762
+ {
2763
+ id: 'smt-shishen',
2764
+ book: 'sanming',
2765
+ chapter: '卷六·论食神',
2766
+ scene: '格局',
2767
+ tags: ['食神'],
2768
+ quote: '凡命遇财煞之地,食神旺相,煞被食制,不敢为祸,财被食生,充裕不竭,故食神一名寿星,一名爵星,良有以也。',
2769
+ plain: '食神旺,一头制煞、一头生财:煞被制住不敢为祸,财被生起不至枯竭;所以食神又叫寿星、爵星。',
2770
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7663.html',
2771
+ },
2772
+ {
2773
+ id: 'smt-shangguan',
2774
+ book: 'sanming',
2775
+ chapter: '卷六·论伤官',
2776
+ scene: '格局',
2777
+ tags: ['伤官'],
2778
+ quote: '伤官虽凶,乃我所生,自家之物,伤尽则能生财,财旺则能生官,造化展转有情。',
2779
+ plain: '伤官虽称凶,却是我自己生出来的:伤尽(柱中不见官星)反能生财,财旺再生官,辗转还是有情。',
2780
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7662.html',
2781
+ },
2782
+ {
2783
+ id: 'smt-yangren',
2784
+ book: 'sanming',
2785
+ chapter: '卷六·论阳刃',
2786
+ scene: '格局',
2787
+ tags: ['阳刃', '劫财'],
2788
+ quote: '阳者,阴阳之阳,刃者,刀刃之刃,即禄前一位,言旺越其分,故险。',
2789
+ plain: '阳刃指禄前一位,旺过了本分,所以险;甲丙戊庚壬五阳干才有刃,阴干见伤官同论。',
2790
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7671.html',
2791
+ },
2792
+ {
2793
+ id: 'smt-jianlu',
2794
+ book: 'sanming',
2795
+ chapter: '卷六·论建禄',
2796
+ scene: '格局',
2797
+ tags: ['建禄', '比肩', '月劫'],
2798
+ quote: '凡命月令建禄,难招祖业,必主平生见财不聚,却病少寿长,行运再见比肩,克妻妨父损子',
2799
+ plain: '月令建禄是日主临官之地,身旺而财官难取:古说难承祖业、见财不聚,但病少寿长;再走比肩运,古书说克妻妨父损子(属于旧说的重话,本插件只记原文、不作断语)。',
2800
+ url: 'https://www.diancang.xyz/xuanxuewushu/sanmingtonghui/7672.html',
2801
+ },
2710
2802
  ];
2711
2803
 
2712
2804
  const BY_ID = CLASSICS.reduce((m, e) => { m[e.id] = e; return m; }, {});
@@ -2737,7 +2829,15 @@ function tagOf(gejuName) {
2737
2829
  return GEJU_KEY[bare + '格'] || bare;
2738
2830
  }
2739
2831
 
2740
- function classicsFor(r, limit = 4) {
2832
+ function pick(tag, book) {
2833
+ return CLASSICS.find((e) => e.scene === '格局' && e.tags.includes(tag) && (!book || e.book === book)) || null;
2834
+ }
2835
+
2836
+ function chapterTitle(e) {
2837
+ return e.chapter.replace(/^.*[、·]/, '').replace(/^论/, '');
2838
+ }
2839
+
2840
+ function classicsFor(r, limit = 5) {
2741
2841
  if (!r) return [];
2742
2842
  const out = [];
2743
2843
  const seen = new Set();
@@ -2750,8 +2850,10 @@ function classicsFor(r, limit = 4) {
2750
2850
  for (const g of gejuList) {
2751
2851
  if (!g || (g.kind && g.kind !== '月令格')) continue;
2752
2852
  const tag = tagOf(g.name);
2753
- const hit = CLASSICS.find((e) => e.scene === '格局' && e.tags.includes(tag));
2754
- if (hit) push(hit, `你的月令格局是「${g.name}」,故取${CLASSIC_BOOKS[hit.book].name}论${hit.chapter.replace(/^.*、/, '')}`);
2853
+ const primary = pick(tag, 'ziping') || pick(tag);
2854
+ if (primary) push(primary, `你的月令格局是「${g.name}」,故取${CLASSIC_BOOKS[primary.book].name}「${chapterTitle(primary)}」一条`);
2855
+ const alt = CLASSICS.find((e) => e.scene === '格局' && e.tags.includes(tag) && (!primary || e.book !== primary.book));
2856
+ if (alt) push(alt, `同是「${g.name}」,${CLASSIC_BOOKS[alt.book].name}「${chapterTitle(alt)}」另有取法,可对照读`);
2755
2857
  }
2756
2858
  const cli = r.climate || (r.frame && r.frame.yongshen && (r.frame.yongshen.climate || r.frame.yongshen.tiaohou));
2757
2859
  if (cli && cli.kind && cli.kind !== '平和') {
@@ -2761,7 +2863,15 @@ function classicsFor(r, limit = 4) {
2761
2863
  push(classicOf('zp-qihou'), '气候与月令用神要互参,故取子平真诠「用神配气候」一条');
2762
2864
  if (hot) push(classicOf('dt-hannuan'), '本盘偏热,寒暖一条正对');
2763
2865
  }
2764
- push(classicOf('zp-yongshen'), '用神从月令取、四吉顺用四凶逆用,是通用总纲');
2866
+ const huagi = gejuList.find((g) => g && g.kind === '化气格');
2867
+ const combo = gejuList.find((g) => g && g.kind === '组合');
2868
+ if (huagi) {
2869
+ push(classicOf('zp-bianhua'), `本盘列有「${huagi.name}」:用神由月令合化而变,总纲一位改取${CLASSIC_BOOKS.ziping.name}论用神变化`);
2870
+ } else if (combo) {
2871
+ push(classicOf('zp-chengbai'), `本盘另有「${combo.name}」组合:成格还要看带忌与救应,总纲一位改取${CLASSIC_BOOKS.ziping.name}论用神成败救应`);
2872
+ } else {
2873
+ push(classicOf('zp-yongshen'), '用神从月令取、四吉顺用四凶逆用,是通用总纲');
2874
+ }
2765
2875
  return out;
2766
2876
  }
2767
2877
 
@@ -3367,9 +3477,12 @@ function buildBazi(input) {
3367
3477
  };
3368
3478
 
3369
3479
  const dayun = [];
3480
+ const dayunWindows = [];
3370
3481
  for (let i = 1; i <= 8; i++) {
3371
3482
  const idx = (((monthIdx + (forward ? i : -i)) % 60) + 60) % 60;
3372
3483
  const g = ganzhiName(idx);
3484
+ const fromJd = partsToJd(addYearsClamped(jiaoParts, (i - 1) * 10));
3485
+ const toJd = partsToJd(addYearsClamped(jiaoParts, i * 10));
3373
3486
  dayun.push({
3374
3487
  step: i,
3375
3488
  startAge: startAgeYears + (i - 1) * 10,
@@ -3378,9 +3491,10 @@ function buildBazi(input) {
3378
3491
  branch: g.branch,
3379
3492
  stemTenGod: tenGod(dayStem, g.stem),
3380
3493
  nayin: nayin(idx),
3381
- fromDate: formatJd(partsToJd(addYearsClamped(jiaoParts, (i - 1) * 10))),
3382
- toDate: formatJd(partsToJd(addYearsClamped(jiaoParts, i * 10))),
3494
+ fromDate: formatJd(fromJd),
3495
+ toDate: formatJd(toJd),
3383
3496
  });
3497
+ dayunWindows.push({ step: i, ganzhi: g.name, fromJd, toJd });
3384
3498
  }
3385
3499
 
3386
3500
  const thisYear = Number(input.nowYear) || new Date().getFullYear();
@@ -3521,6 +3635,45 @@ function buildBazi(input) {
3521
3635
  l.luck = scoreLuck(l.ganzhi, frame);
3522
3636
  }
3523
3637
 
3638
+ const NATAL_LABEL = { year: '年柱', month: '月柱', day: '日柱', hour: '时柱' };
3639
+ const stemOvercomes = (a, b) => OVERCOMES[STEM_ELEMENT[a]] === STEM_ELEMENT[b];
3640
+ const tianKeDiChong = (a, b) => {
3641
+ if (typeof a !== 'string' || typeof b !== 'string' || a.length < 2 || b.length < 2) return false;
3642
+ if (!(a[0] in STEM_ELEMENT) || !(b[0] in STEM_ELEMENT)) return false;
3643
+ return (stemOvercomes(a[0], b[0]) || stemOvercomes(b[0], a[0])) && hasPair(BRANCH_CHONG, a[1], b[1]);
3644
+ };
3645
+ const lichunJd = (y) => {
3646
+ const t = termWindow(y, { jieOnly: true }).find((x) => x.index === 2 && jdToParts(x.jd).year === y);
3647
+ return t ? t.jd : partsToJd({ year: y, month: 2, day: 4 });
3648
+ };
3649
+ for (const l of liunian) {
3650
+ const yStart = lichunJd(l.year);
3651
+ const yEnd = lichunJd(l.year + 1);
3652
+ const active = dayunWindows.filter((w) => w.fromJd < yEnd && w.toJd > yStart);
3653
+ const signals = [];
3654
+ for (const w of active) {
3655
+ if (w.ganzhi === l.ganzhi) {
3656
+ signals.push({ type: '岁运并临', target: `第${w.step}步大运`, detail: `大运与流年同为${l.ganzhi}` });
3657
+ } else if (tianKeDiChong(w.ganzhi, l.ganzhi)) {
3658
+ signals.push({ type: '运年反吟', target: `第${w.step}步大运`, detail: `大运${w.ganzhi}与流年${l.ganzhi}天克地冲` });
3659
+ }
3660
+ }
3661
+ for (const key of ['year', 'month', 'day', 'hour']) {
3662
+ const g = pillars[key].ganzhi;
3663
+ if (!g) continue;
3664
+ if (l.ganzhi === g) {
3665
+ signals.push({ type: '流年伏吟', target: NATAL_LABEL[key], detail: `流年${l.ganzhi}与${NATAL_LABEL[key]}同干支` });
3666
+ } else if (tianKeDiChong(l.ganzhi, g)) {
3667
+ signals.push({ type: '流年反吟', target: NATAL_LABEL[key], detail: `流年${l.ganzhi}与${NATAL_LABEL[key]}${g}天克地冲` });
3668
+ }
3669
+ }
3670
+ l.suiyun = {
3671
+ dayun: active.map((w) => ({ step: w.step, ganzhi: w.ganzhi })),
3672
+ signals,
3673
+ note: '只标干支重复与天克地冲这类变动信号,不判吉凶;大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列',
3674
+ };
3675
+ }
3676
+
3524
3677
  const p = jdToParts(jd);
3525
3678
 
3526
3679
  const mStemIdx = STEMS.indexOf(pillars.month.stem);
@@ -4220,7 +4373,136 @@ function yongshenStrength(result, useGod, opts = {}) {
4220
4373
  };
4221
4374
  }
4222
4375
 
4223
- return { jinTuiOf, hexBody, bodyRelations, bodyLabels, HEXAGRAM_COUNT, hexagramFromBits, YONGSHEN_RULES, yongshenOf, tossLines, buildLiuyao, zhiElementOf, monthStateOf, dayRelationOf, yongshenStrength };
4376
+ function hexagramStrength(result, opts = {}) {
4377
+ const rows = (result && result.ben && Array.isArray(result.ben.lines))
4378
+ ? result.ben.lines
4379
+ : (result && Array.isArray(result.lines) ? result.lines : []);
4380
+ if (!rows.length) return null;
4381
+ const monthBranch = opts.monthBranch || null;
4382
+ const dayBranch = opts.dayBranch || (result.dayGanZhi ? result.dayGanZhi.branch : null);
4383
+ if (!monthBranch && !dayBranch) return null;
4384
+ const monthElement = monthBranch ? zhiElementOf(monthBranch) : null;
4385
+ const dayElement = dayBranch ? zhiElementOf(dayBranch) : null;
4386
+ const kong = Array.isArray(result.xunKong)
4387
+ ? result.xunKong
4388
+ : (typeof result.xunKong === 'string' ? result.xunKong.split('') : []);
4389
+ const shiPos = result.ben ? result.ben.shi : null;
4390
+ const yingPos = result.ben ? result.ben.ying : null;
4391
+
4392
+ const lines = rows.map((l) => {
4393
+ const element = l.element || zhiElementOf(l.branch);
4394
+ const monthChong = !!(monthBranch && CHONG_OF[l.branch] === monthBranch);
4395
+ const dayChong = !!(dayBranch && CHONG_OF[l.branch] === dayBranch);
4396
+ const monthState = monthStateOf(element, monthElement);
4397
+ const dayRelation = dayRelationOf(element, dayElement);
4398
+ const isEmpty = kong.includes(l.branch);
4399
+ return {
4400
+ position: l.position,
4401
+ name: l.name,
4402
+ branch: l.branch,
4403
+ element,
4404
+ liuqin: l.liuqin ?? null,
4405
+ shi: l.position === shiPos,
4406
+ ying: l.position === yingPos,
4407
+ moving: !!l.moving,
4408
+ kong: isEmpty,
4409
+ monthState,
4410
+ monthSay: MONTH_STATE_SAY[monthState] || '',
4411
+ monthChong,
4412
+ dayRelation,
4413
+ daySay: DAY_RELATION_SAY[dayRelation] || '',
4414
+ dayChong,
4415
+ chongTag: monthChong ? '月破'
4416
+ : dayChong ? (isEmpty ? '冲空' : (l.moving ? '逢日辰冲' : '暗动')) : '',
4417
+ };
4418
+ });
4419
+
4420
+ const counts = {};
4421
+ for (const st of MONTH_STATE_ORDER) counts[st] = lines.filter((l) => l.monthState === st).length;
4422
+
4423
+ const labels = [];
4424
+ for (const st of MONTH_STATE_ORDER) if (counts[st]) labels.push(`全卦${st}${counts[st]}爻`);
4425
+ const shiLine = lines.find((l) => l.shi) || null;
4426
+ const yingLine = lines.find((l) => l.ying) || null;
4427
+ if (shiLine && shiLine.monthState !== 'unknown') labels.push(`世爻月建${shiLine.monthState}`);
4428
+ if (yingLine && yingLine.monthState !== 'unknown') labels.push(`应爻月建${yingLine.monthState}`);
4429
+ const tagCount = (tag) => lines.filter((l) => l.chongTag === tag).length;
4430
+ for (const tag of ['月破', '暗动', '冲空', '逢日辰冲']) {
4431
+ const n = tagCount(tag);
4432
+ if (n) labels.push(`${tag}${n}爻`);
4433
+ }
4434
+
4435
+ return {
4436
+ month: monthBranch ? { branch: monthBranch, element: monthElement } : null,
4437
+ day: dayBranch ? { branch: dayBranch, element: dayElement } : null,
4438
+ lines,
4439
+ counts,
4440
+ strongest: lines.filter((l) => l.monthState === '旺').map((l) => l.position),
4441
+ weakest: lines.filter((l) => l.monthState === '死' || l.monthChong).map((l) => l.position),
4442
+ shi: shiLine ? { position: shiLine.position, branch: shiLine.branch, monthState: shiLine.monthState, dayRelation: shiLine.dayRelation } : null,
4443
+ ying: yingLine ? { position: yingLine.position, branch: yingLine.branch, monthState: yingLine.monthState, dayRelation: yingLine.dayRelation } : null,
4444
+ labels,
4445
+ note: '月建主这一段的旺衰、日辰管当天的扶克,六爻逐一给状态;判定与用神旺衰同一套,只多了世应与动爻标记。只报状态、不折分数,也不单独断吉凶。',
4446
+ };
4447
+ }
4448
+
4449
+ const JIN_VERDICT = { 旺: '后劲足', 相: '后劲足', 休: '后劲有限', 囚: '后劲有限', 死: '进得虚' };
4450
+ const TUI_VERDICT = { 旺: '退得缓', 相: '退得缓', 休: '退得实', 囚: '退得实', 死: '退得实' };
4451
+
4452
+ function jinTuiPower(result, opts = {}) {
4453
+ const items = result && result.body && Array.isArray(result.body.jinTui) ? result.body.jinTui : [];
4454
+ if (!items.length) return null;
4455
+ const rows = result && result.ben && Array.isArray(result.ben.lines) ? result.ben.lines : [];
4456
+ const monthBranch = opts.monthBranch || null;
4457
+ const dayBranch = opts.dayBranch || (result && result.dayGanZhi ? result.dayGanZhi.branch : null);
4458
+ const monthElement = monthBranch ? zhiElementOf(monthBranch) : null;
4459
+ const dayElement = dayBranch ? zhiElementOf(dayBranch) : null;
4460
+ const kong = Array.isArray(result && result.xunKong)
4461
+ ? result.xunKong
4462
+ : (result && typeof result.xunKong === 'string' ? result.xunKong.split('') : []);
4463
+
4464
+ const lines = items.map((x) => {
4465
+ const line = rows[x.position - 1] || null;
4466
+ const element = (line && line.element) || zhiElementOf(x.from);
4467
+ const monthState = monthStateOf(element, monthElement);
4468
+ const monthChong = !!(monthBranch && CHONG_OF[x.from] === monthBranch);
4469
+ const dayChong = !!(dayBranch && CHONG_OF[x.from] === dayBranch);
4470
+ const dayRelation = dayRelationOf(element, dayElement);
4471
+ const isEmpty = kong.includes(x.from);
4472
+ const verdict = !monthBranch ? '只报趋势'
4473
+ : monthChong ? '月破失据'
4474
+ : ((x.type === '进' ? JIN_VERDICT : TUI_VERDICT)[monthState] || '未定');
4475
+ const monthSay = monthBranch
4476
+ ? `月建${monthBranch}(${monthElement})对${element}为${monthState}`
4477
+ : '缺月建,只看日辰';
4478
+ const daySay = dayRelation === 'unknown' ? '' : `日辰${dayBranch}(${dayElement})${dayRelation}`;
4479
+ const extra = [monthChong ? '又遭月破' : '', isEmpty ? '旬空待填' : '', dayChong ? '逢日辰冲' : ''].filter(Boolean);
4480
+ return {
4481
+ position: x.position,
4482
+ from: x.from,
4483
+ to: x.to,
4484
+ type: x.type,
4485
+ element,
4486
+ monthState,
4487
+ monthChong,
4488
+ dayRelation,
4489
+ dayChong,
4490
+ kong: isEmpty,
4491
+ verdict,
4492
+ say: `${x.from}→${x.to}(${x.type}):${monthSay}${daySay ? `,${daySay}` : ''}${extra.length ? `,${extra.join('、')}` : ''} → ${verdict}`,
4493
+ };
4494
+ });
4495
+
4496
+ return {
4497
+ month: monthBranch ? { branch: monthBranch, element: monthElement } : null,
4498
+ day: dayBranch ? { branch: dayBranch, element: dayElement } : null,
4499
+ lines,
4500
+ labels: lines.map((l) => `${l.position}爻化${l.type}·${l.verdict}`),
4501
+ note: '进退只说这一爻往哪边走;力重是把它放回月建日辰,看有没有托着——得令则进得动、退得慢,失令则进得虚、退得实,月破压过一切。仍不直接定吉凶:用神化进为喜,忌神化进反而是忌。',
4502
+ };
4503
+ }
4504
+
4505
+ return { jinTuiOf, hexBody, bodyRelations, bodyLabels, HEXAGRAM_COUNT, hexagramFromBits, YONGSHEN_RULES, yongshenOf, tossLines, buildLiuyao, zhiElementOf, monthStateOf, dayRelationOf, yongshenStrength, hexagramStrength, jinTuiPower };
4224
4506
  })();
4225
4507
 
4226
4508
  __mods.ziwei = (function () {
@@ -4697,6 +4979,7 @@ function buildZiwei(input) {
4697
4979
  shensha: '长生、博士随大限顺逆;将前、岁前恒顺行;天伤恒在交友、天使恒在疾厄',
4698
4980
  masters: '命主按命宫地支,身主按生年地支',
4699
4981
  liunian: '流年命宫取该年地支宫;三方四正为本宫与 +4/+8/+6;并给该年所属大限与小限',
4982
+ flyComposite: '三方四正取本宫、对宫与两个三合宫共四宫,四宫宫干各化禄权科忌共 16 条,按落宫归堆标会聚(双忌会、禄忌同宫、三化会、自化);只描会照,不折吉凶',
4700
4983
  changqu: '昌曲落宫只由时支决定,故与丑未的关系(夹/三合/同宫/无关)是结构性结论;absorb 的「清/杂」是启发式标记,依据昌曲化忌与会煞,不是断语',
4701
4984
  },
4702
4985
  };
@@ -4705,13 +4988,15 @@ function buildZiwei(input) {
4705
4988
  chart.daxianList = daxianList(chart);
4706
4989
  chart.fly = flyStars(chart);
4707
4990
  chart.flyChains = flyChains(chart, { from: '命宫' });
4991
+ chart.flyComposite = flyComposite(chart, { from: '命宫' });
4708
4992
  enrichLiunian(chart);
4709
4993
  chart.flow = flowOf(chart, input.asOf);
4710
4994
  chart.patternSummary = {
4711
4995
  count: chart.patterns.length,
4712
4996
  good: chart.patterns.filter((p) => p.tone === 'good').map((p) => p.name),
4997
+ neutral: chart.patterns.filter((p) => p.tone === 'neutral').map((p) => p.name),
4713
4998
  bad: chart.patterns.filter((p) => p.tone === 'bad').map((p) => p.name),
4714
- note: '格局只描述结构,不作吉凶断语;同一命盘可并存吉格与忌格',
4999
+ note: '格局只描述结构,不作吉凶断语;同一命盘可并存吉格、中性结构与忌格',
4715
5000
  };
4716
5001
  return chart;
4717
5002
  }
@@ -4751,7 +5036,7 @@ function detectPatterns(chart) {
4751
5036
 
4752
5037
  const lu = starBranchOf(chart, '禄存');
4753
5038
  const ma = starBranchOf(chart, '天马');
4754
- if (lu >= 0 && ma >= 0 && (lu === ma || fix(lu - ma) === 6)) add('禄马交驰', `禄存在${chart.ziwei && BRANCHES[lu]},天马在${BRANCHES[ma]}`);
5039
+ if (lu >= 0 && ma >= 0 && (lu === ma || fix(lu - ma) === 6)) add('禄马交驰', `禄存在${BRANCHES[lu]},天马在${BRANCHES[ma]}`);
4755
5040
 
4756
5041
  const huo = starBranchOf(chart, '火星');
4757
5042
  const ling = starBranchOf(chart, '铃星');
@@ -4759,11 +5044,8 @@ function detectPatterns(chart) {
4759
5044
  if (fix(soulIdx - 1) === ling && fix(soulIdx + 1) === huo) add('火铃夹命', '铃星与火星夹命');
4760
5045
 
4761
5046
  const ji = chart.palaces.find((p) => p.stars.some((s) => s.mutagen === '忌'));
4762
- if (ji) {
4763
- const jb = BRANCHES.indexOf(ji.branch);
4764
- if (fix(jb - 1) === starBranchOf(chart, '擎羊') && fix(jb + 1) === starBranchOf(chart, '陀罗')) {
4765
- add('羊陀夹忌', `化忌在${ji.branch},擎羊陀罗夹之`);
4766
- }
5047
+ if (ji && lu >= 0 && BRANCHES.indexOf(ji.branch) === lu) {
5048
+ add('羊陀夹忌', `化忌与禄存在${ji.branch}同宫,擎羊陀罗夹之`);
4767
5049
  }
4768
5050
 
4769
5051
  const lctw = ['铃星', '文昌', '陀罗', '武曲'].map((n) => starBranchOf(chart, n));
@@ -4782,7 +5064,9 @@ function detectPatterns(chart) {
4782
5064
  const jia = (a, b) => {
4783
5065
  const ba = starBranchOf(chart, a);
4784
5066
  const bb = starBranchOf(chart, b);
4785
- return ba >= 0 && bb >= 0 && fix(ba + 1) === soulIdx && fix(bb - 1) === soulIdx;
5067
+ if (ba < 0 || bb < 0) return false;
5068
+ return (fix(ba + 1) === soulIdx && fix(bb - 1) === soulIdx)
5069
+ || (fix(bb + 1) === soulIdx && fix(ba - 1) === soulIdx);
4786
5070
  };
4787
5071
  const mutagensIn = (indices) => {
4788
5072
  const set = new Set();
@@ -4813,7 +5097,7 @@ function detectPatterns(chart) {
4813
5097
  if (jia('左辅', '右弼')) add('左右夹命', '左辅、右弼夹命宫');
4814
5098
  if (jia('文昌', '文曲')) add('昌曲夹命', '文昌、文曲夹命宫');
4815
5099
  if (jia('天魁', '天钺')) add('魁钺夹命', '天魁、天钺夹命宫');
4816
- if (jia('擎羊', '陀罗')) add('羊陀夹命', '擎羊、陀罗夹命宫');
5100
+ if (jia('擎羊', '陀罗')) add('羊陀夹命', `禄存在${BRANCHES[soulIdx]}守命,擎羊陀罗前后夹之`);
4817
5101
 
4818
5102
  const muts = mutagensIn(sanfang);
4819
5103
  if (muts.has('禄') && muts.has('权') && muts.has('科')) add('三奇嘉会', '化禄、化权、化科会于命宫三方四正');
@@ -4826,9 +5110,10 @@ function detectPatterns(chart) {
4826
5110
  const lianzhen = starBranchOf(chart, '廉贞');
4827
5111
  const tianxiang = starBranchOf(chart, '天相');
4828
5112
  const qingyang = starBranchOf(chart, '擎羊');
4829
- if (lianzhen >= 0 && qingyang >= 0 && tianxiang >= 0
4830
- && fix(lianzhen + 1) === tianxiang && fix(qingyang - 1) === tianxiang) {
4831
- add('刑囚夹印', '廉贞与擎羊夹天相(刑囚夹印)');
5113
+ if (lianzhen >= 0 && tianxiang >= 0 && qingyang >= 0
5114
+ && lianzhen === tianxiang && qingyang === tianxiang && tianxiang === soulIdx
5115
+ && (tianxiang === 0 || tianxiang === 6)) {
5116
+ add('刑囚夹印', `廉贞天相同守命宫${BRANCHES[tianxiang]},擎羊同宫`);
4832
5117
  }
4833
5118
  if (near.has('贪狼') && (near.has('火星') || near.has('铃星'))) {
4834
5119
  add('火贪铃贪', `贪狼与${near.has('火星') ? '火星' : '铃星'}会于命宫三方四正`);
@@ -4949,6 +5234,97 @@ function flyChains(chart, { from = '命宫', maxSteps = 12 } = {}) {
4949
5234
  return out;
4950
5235
  }
4951
5236
 
5237
+ const PALACE_SUFFIX = (p) => `${p}${String(p).endsWith('宫') ? '' : '宫'}`;
5238
+
5239
+ function flyComposite(chart, { from = '命宫' } = {}) {
5240
+ const palaces = chart.palaces || [];
5241
+ const src = palaces.find((p) => p.palace === from) || palaces[0];
5242
+ if (!src) return null;
5243
+ const names = Array.isArray(src.sanfang) && src.sanfang.length
5244
+ ? [...new Set(src.sanfang)].slice(0, 4)
5245
+ : [src.palace];
5246
+
5247
+ const sources = [];
5248
+ for (const name of names) {
5249
+ const p = palaces.find((x) => x.palace === name);
5250
+ if (!p) continue;
5251
+ sources.push({
5252
+ palace: p.palace,
5253
+ branch: p.branch,
5254
+ stem: p.stem,
5255
+ flies: FLY_TYPES.map((type) => {
5256
+ const star = flyStarOf(p.stem, type);
5257
+ const b = star ? starBranchOf(chart, star) : -1;
5258
+ const target = b >= 0 ? palaces.find((x) => x.branch === BRANCHES[b]) : null;
5259
+ return {
5260
+ type,
5261
+ star,
5262
+ to: target ? target.palace : null,
5263
+ toBranch: target ? target.branch : null,
5264
+ self: !!(target && target.palace === p.palace),
5265
+ };
5266
+ }),
5267
+ });
5268
+ }
5269
+
5270
+ const targets = [];
5271
+ for (const p of palaces) {
5272
+ const incoming = [];
5273
+ for (const s of sources) {
5274
+ for (const f of s.flies) {
5275
+ if (f.to === p.palace) {
5276
+ incoming.push({ from: s.palace, fromBranch: s.branch, fromStem: s.stem, type: f.type, star: f.star, self: f.self });
5277
+ }
5278
+ }
5279
+ }
5280
+ if (!incoming.length) continue;
5281
+ targets.push({
5282
+ palace: p.palace,
5283
+ branch: p.branch,
5284
+ incoming,
5285
+ types: [...new Set(incoming.map((x) => x.type))],
5286
+ count: incoming.length,
5287
+ });
5288
+ }
5289
+ targets.sort((a, b) => b.count - a.count || BRANCHES.indexOf(a.branch) - BRANCHES.indexOf(b.branch));
5290
+
5291
+ const jiAt = (t) => t.incoming.filter((x) => x.type === '忌').length;
5292
+ const groups = {
5293
+ doubleJi: targets.filter((t) => jiAt(t) >= 2),
5294
+ luJi: targets.filter((t) => t.types.includes('禄') && t.types.includes('忌')),
5295
+ tripleHua: targets.filter((t) => t.types.length >= 3),
5296
+ selfHua: targets.filter((t) => t.incoming.some((x) => x.self)),
5297
+ };
5298
+
5299
+ const labels = [];
5300
+ for (const t of groups.doubleJi) labels.push(`${['', '', '双忌', '三忌', '四忌'][jiAt(t)] || `${jiAt(t)}忌`}会${PALACE_SUFFIX(t.palace)}`);
5301
+ for (const t of groups.luJi) labels.push(`禄忌同宫${PALACE_SUFFIX(t.palace)}`);
5302
+ const HUA_NUM = { 3: '三化会', 4: '四化会' };
5303
+ for (const t of groups.tripleHua) labels.push(`${HUA_NUM[t.types.length] || `${t.types.length}化会`}${PALACE_SUFFIX(t.palace)}`);
5304
+ for (const t of groups.selfHua) labels.push(`${PALACE_SUFFIX(t.palace)}自化`);
5305
+
5306
+ const byType = {};
5307
+ for (const type of FLY_TYPES) {
5308
+ byType[type] = sources
5309
+ .map((s) => {
5310
+ const f = s.flies.find((x) => x.type === type);
5311
+ return f && f.to ? { from: s.palace, star: f.star, to: f.to, self: f.self } : null;
5312
+ })
5313
+ .filter(Boolean);
5314
+ }
5315
+
5316
+ return {
5317
+ from: src.palace,
5318
+ sanfang: sources.map((s) => s.palace),
5319
+ sources,
5320
+ targets,
5321
+ groups,
5322
+ byType,
5323
+ labels,
5324
+ note: '三方四正取本宫、对宫与两个三合宫共四宫,四宫宫干各化禄权科忌共 16 条飞化,按落宫归堆;只在落宫相同(会照)时标会聚,单条飞出不另立标签。只描会照与方向,不折吉凶,也未叠大限流年四化。',
5325
+ };
5326
+ }
5327
+
4952
5328
  function enrichLiunian(chart) {
4953
5329
  for (const ln of chart.liunian) {
4954
5330
  const stem = ln.ganzhi[0];
@@ -5051,7 +5427,7 @@ function flowOf(chart, asOf) {
5051
5427
  };
5052
5428
  }
5053
5429
 
5054
- return { PALACE_NAMES, BRIGHTNESS, MUTAGEN, 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, buildZiwei, detectPatterns, daxianList, FLY_TYPES, flyStarOf, flyStars, flyChain, flyChains, enrichLiunian, asOfParts, flowOf };
5430
+ return { PALACE_NAMES, BRIGHTNESS, MUTAGEN, 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, buildZiwei, detectPatterns, PATTERN_TONE, daxianList, FLY_TYPES, flyStarOf, flyStars, flyChain, flyChains, flyComposite, enrichLiunian, asOfParts, flowOf };
5055
5431
  })();
5056
5432
 
5057
5433
  __mods.tarot = (function () {
@@ -9463,6 +9839,40 @@ function useStrengthPlain(st) {
9463
9839
  return `${when}对这件事有帮有压,一半的力要自己挣——结果更看你怎么走。`;
9464
9840
  }
9465
9841
 
9842
+ function jinTuiPlain(r) {
9843
+ const jp = r && r.jinTuiPower;
9844
+ if (!jp || !Array.isArray(jp.lines) || !jp.lines.length) return '';
9845
+ const sayOf = (l) => (l.verdict === '后劲足' ? '时令托着,后劲足'
9846
+ : l.verdict === '后劲有限' ? '时令不太托,后劲有限'
9847
+ : l.verdict === '进得虚' ? '时令最背,进也是虚进'
9848
+ : l.verdict === '退得缓' ? '时令还托着,退得慢'
9849
+ : l.verdict === '退得实' ? '时令也不托,退得实'
9850
+ : l.verdict === '月破失据' ? '又正好逢月破,进退都站不稳'
9851
+ : '缺月建,先只当方向看');
9852
+ return `动爻的进退还要看时令:${jp.lines.map((l) => `${l.position}爻${l.from}→${l.to}${l.type},${sayOf(l)}`).join(';')}。`;
9853
+ }
9854
+
9855
+ function hexStrengthPlain(hs) {
9856
+ if (!hs || !hs.counts) return '';
9857
+ const up = (hs.counts.旺 || 0) + (hs.counts.相 || 0);
9858
+ const down = (hs.counts.囚 || 0) + (hs.counts.死 || 0);
9859
+ const when = hs.month && hs.month.branch ? `按${hs.month.branch}月这段时令看` : '按起卦这天看';
9860
+ const shi = hs.shi && hs.shi.monthState !== 'unknown' ? `代表你的世爻在${hs.shi.monthState}` : '';
9861
+ if (up >= 4 && down === 0) return `${when},整张卦六爻的气多数是顺的,${shi},眼下算是好推的时候。`;
9862
+ if (down >= 4 && up === 0) return `${when},整张卦六爻的气多数被压着,${shi},这几天更适合等而不是冲。`;
9863
+ return `${when},整张卦六爻有顺有逆(旺相 ${up} 爻、囚死 ${down} 爻)${shi ? `,${shi}` : ''},成不成要看具体哪一爻在动。`;
9864
+ }
9865
+
9866
+ function flyCompositePlain(fc) {
9867
+ if (!fc || !Array.isArray(fc.targets)) return '';
9868
+ const hub = fc.targets.find((t) => t.count >= 2);
9869
+ const self = fc.groups && fc.groups.selfHua && fc.groups.selfHua.length
9870
+ ? '其中有的宫自己化出去又落回自己,容易表现为「想得多、落得少」。'
9871
+ : '';
9872
+ if (!hub) return `把命宫连同对宫、两个三合宫一起看,四股力飞出去没有挤到同一处,这几件事之间牵连不深。${self}`;
9873
+ return `把命宫连同对宫与两个三合宫一起看,${hub.incoming.length} 股力最后挤在「${String(hub.palace).replace(/宫$/, '')}」这件事上——这件事会把好几个方面的劲都吸过来。${self}`;
9874
+ }
9875
+
9466
9876
  function bodyPlainOf(labels) {
9467
9877
  const keyOf = (l) => Object.keys(PLAIN_BODY).find((k) => l === k || l.startsWith(`${k} `)) || '';
9468
9878
  const primary = labels.find((l) => keyOf(l));
@@ -9525,6 +9935,7 @@ function buildAnswer(r, ask, ctx = {}) {
9525
9935
  `${bureau.name ? `、${bureau.name}` : ''}${md ? `,命主${md}` : ''}` +
9526
9936
  `${body.lands ? `,身宫落在「${body.lands}」——这张盘的重心在这件事上` : ''}。`,
9527
9937
  plainFly,
9938
+ flyCompositePlain(r.flyComposite),
9528
9939
  `具体到你问的这件事,重点看${where}。`,
9529
9940
  ].filter(Boolean).join('');
9530
9941
  }
@@ -9539,7 +9950,7 @@ function buildAnswer(r, ask, ctx = {}) {
9539
9950
  head + `先说结论:本卦是${s(ben.name, '—')}` +
9540
9951
  `${ben.palace ? `(${ben.palace})` : ''}` +
9541
9952
  `${moving.length ? `,第 ${moving.join('、')} 爻动,变为${s(bian.name, '—')}` : ',六爻安静没有动爻'}。` +
9542
- `${plainBody}${usePlainOf(r.useGod)}${useStrengthPlain(r.useGodStrength)}`,
9953
+ `${plainBody}${usePlainOf(r.useGod)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${hexStrengthPlain(r.hexStrength)}`,
9543
9954
  '断卦的落点在「世爻」(代表你)和「动爻」(事情的推动点),下面「看盘顺序」就按这个次序展开。',
9544
9955
  ].join('');
9545
9956
  }
@@ -9556,9 +9967,13 @@ function buildPlain(r, ask, ctx = {}) {
9556
9967
  const avoid = join(ys.avoid);
9557
9968
  const strength = STRENGTH_PLAIN[st.level] || '力量偏中性';
9558
9969
  const tiaohou = ys.tiaohou && ys.tiaohou.note ? `另外,${ys.tiaohou.note},所以「暖一点、亮一点」的环境通常更舒服。` : '';
9970
+ const sy = (r.liunian || []).find((l) => l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
9971
+ const syPlain = sy
9972
+ ? `再往后看,${sy.year} 年${sy.ganzhi}是${sy.suiyun.signals.map((x) => x.type).join('、')}——传统上把它当作「变化感强一点」的提示,既不是好消息也不是坏消息,遇上时多留个心眼就好。`
9973
+ : '';
9559
9974
  return `你的命局属于「${s(st.level, '中性')}」:${strength}。` +
9560
9975
  `${favor ? `能补${favor}的环境(行业、方位、作息、相处的人)会让你省力;${avoid ? `而${avoid}偏重的场合容易空耗,能绕就绕。` : ''}` : ''}` +
9561
- tiaohou;
9976
+ tiaohou + syPlain;
9562
9977
  }
9563
9978
  if (r.system === 'ziwei') {
9564
9979
  const soul = r.soul || {};
@@ -9571,6 +9986,7 @@ function buildPlain(r, ask, ctx = {}) {
9571
9986
  `紫微看的是「你与环境怎么互动」。命宫落${s(soul.branch, '—')}宫,说明你做事的第一反应是从这个宫位的气质出发` +
9572
9987
  `${body.lands ? `;身宫在「${body.lands}」,意味着真正让你上心的,是${body.lands}这件事` : ''}。`,
9573
9988
  plainFly,
9989
+ flyCompositePlain(r.flyComposite),
9574
9990
  ].filter(Boolean).join('');
9575
9991
  }
9576
9992
  if (r.system === 'liuyao') {
@@ -9580,7 +9996,7 @@ function buildPlain(r, ask, ctx = {}) {
9580
9996
  const plainBody = bodyPlainOf(labels);
9581
9997
  return `${s(ben.name, '这一卦')}讲的是一个「${moving.length ? '正在动、还会变' : '暂时不动、先看清'}」的局面——` +
9582
9998
  `${moving.length ? '有动爻,说明事情还有转圜空间,不是死局。' : '六爻安静,说明当下更该观察而不是硬推。'}` +
9583
- `${plainBody}${usePlainOf(r.useGod)}${useStrengthPlain(r.useGodStrength)}`;
9999
+ `${plainBody}${usePlainOf(r.useGod)}${useStrengthPlain(r.useGodStrength)}${jinTuiPlain(r)}${hexStrengthPlain(r.hexStrength)}`;
9584
10000
  }
9585
10001
  return '';
9586
10002
  }
@@ -9721,10 +10137,10 @@ const KNOWLEDGE = [
9721
10137
  id: 'bazi-suiyun',
9722
10138
  system: 'bazi', dims: ['overall', 'career'],
9723
10139
  gist: '大运管十年基调,流年管当年触发。',
9724
- detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(大运与流年干支相同)与伏吟反吟(干支重复或与原局对冲)是传统上标记「变动较大」的年份,属于提示而非断语。',
10140
+ detail: '大运像换了条路,流年像这条路上某一段的天气。岁运并临(当值大运与流年干支相同)、运年反吟(大运与流年天克地冲)、流年伏吟(流年与原局某柱干支相同)与流年反吟(流年与原局某柱天克地冲),是传统上标记「变动较大」的年份,属于提示而非断语。本插件逐年标出四类信号:当值大运按该年立春到次年立春的区间取,交运落在年内时前后两步并列;只标干支重复与对冲,不折分数。',
9725
10141
  source: '岁运并临、伏吟反吟见《三命通会》《渊海子平》',
9726
10142
  status: 'variant',
9727
- note: '「岁运并临主大凶」是流传最广也最被滥用的说法,本插件只作变动提示,不断吉凶。',
10143
+ note: '「岁运并临主大凶」是流传最广也最被滥用的说法,本插件只作变动提示,不断吉凶;大运与原局之间的伏吟反吟未逐年标注,四柱十神层面的引化也未叠入。',
9728
10144
  },
9729
10145
  {
9730
10146
  id: 'bazi-liuqin',
@@ -9753,6 +10169,34 @@ const KNOWLEDGE = [
9753
10169
  status: 'settled',
9754
10170
  },
9755
10171
 
10172
+ {
10173
+ id: 'bazi-sanming-duizhao',
10174
+ system: 'bazi', dims: ['overall', 'career'],
10175
+ gist: '同一个格,子平真诠讲怎么用,三命通会讲怎么取。',
10176
+ detail: '两书不是谁替代谁:子平真诠以月令取用神、论顺逆成败,重「用」;三命通会按十神逐格立论,重「取」与「忌」。同看正官格,前者问财印护官、伤煞混杂,后者先说「止许一位,多则不宜」。本插件把两条并列,不合并成一句断语。',
10177
+ source: '《子平真诠》论正官等篇;《三命通会》卷五、卷六论十神',
10178
+ status: 'variant',
10179
+ note: '两书成书年代与流派不同,用语有出入;本插件照录通行本,不做校勘改字,也不替两家调和。',
10180
+ },
10181
+ {
10182
+ id: 'bazi-geju-chengbai',
10183
+ system: 'bazi', dims: ['overall', 'career'],
10184
+ gist: '格有成有败:成中带忌会打折,败中有救还能转成。',
10185
+ detail: '月令取到格只是起点。成格要看有没有带忌——官逢财又逢伤、官透而被合、财旺生官又逢伤,都会把成格打折;败格也看有没有救应——印来解伤、合来清煞、劫来解财,败中可转。所以判格不能只报一个格名。',
10186
+ source: '《子平真诠》九、论用神成败救应',
10187
+ status: 'variant',
10188
+ note: '各家对成格条件与带忌轻重的排序不同(有先看刑冲、有先看合化),本插件只把原文与成败方向并列摆出,不逐盘判成破。',
10189
+ },
10190
+ {
10191
+ id: 'bazi-huagi-bianhua',
10192
+ system: 'bazi', dims: ['overall', 'career'],
10193
+ gist: '月令用神会因合化而变:同一个起点,取用可以不同。',
10194
+ detail: '用神本从月令取,但月令藏干不一,透出的字不同、支会合局不同,取用就跟着变:丁生亥月本为正官,支全卯未化为印;己生申月本属伤官,藏庚透壬化为财。所以看到化气格,要回头核对月令与合局,不能只按本气定格。',
10195
+ source: '《子平真诠》十、论用神变化;化气条件各家另论',
10196
+ status: 'variant',
10197
+ note: '化气格成立条件各家宽严不一(是否要求月令为化神、日主是否允许有根),本插件只列条件、附原文,不据此断定化气已成。',
10198
+ },
10199
+
9756
10200
  {
9757
10201
  id: 'ziwei-sanfang',
9758
10202
  system: 'ziwei', dims: ['overall', 'career'],
@@ -9812,7 +10256,16 @@ const KNOWLEDGE = [
9812
10256
  detail: '生年四化是先天带的,飞星四化是宫位自己去找的:每一宫的宫干定出禄权科忌四颗星,那几颗星落在哪一宫,就是这一宫的力气往哪边使。所以同一个「化忌」,从命宫飞出去和从财帛飞出去,说的是两件事。',
9813
10257
  source: '《紫微斗数全书》四化篇;中州派宫干四化传法',
9814
10258
  status: 'variant',
9815
- note: '飞星派(如钦天四化)把宫干四化当主线,三合派只作辅助;庚干四化各派另有异说,本插件取中州派常用表。',
10259
+ note: '飞星派(如钦天四化)把宫干四化当主线,三合派只作辅助;庚干四化各派另有异说,本插件取中州派常用表。飞星串连与三方四正复合飞化现已给出,但未叠大限流年四化。',
10260
+ },
10261
+ {
10262
+ id: 'ziwei-sanfang-fei',
10263
+ system: 'ziwei', dims: ['overall', 'career'],
10264
+ gist: '三方四正一起飞,看四股力会到哪一宫。',
10265
+ detail: '单宫飞化只看本宫宫干把四化送去哪里;三方四正复合飞化把本宫、对宫与两个三合宫共四宫的宫干四化同时摊开,约 16 条,按落宫归堆,落到同一宫的就是会照。会在一处的化越多,那一宫被牵动的力道越集中;两忌以上会宫、禄忌同宫、三化以上会宫、宫干化出又落回本宫(自化),是这一层最常取的几种情形。',
10266
+ source: '《紫微斗数全书》四化篇;飞星派三方四正同飞说',
10267
+ status: 'variant',
10268
+ note: '飞星派对「三方四正一起飞」是否成立、会照要不要折成分数,各家口径不一;本插件只做落宫归堆与会照标记,不折分数,也未叠大限流年四化。',
9816
10269
  },
9817
10270
  {
9818
10271
  id: 'ziwei-jichuanji',
@@ -9866,6 +10319,15 @@ const KNOWLEDGE = [
9866
10319
  status: 'variant',
9867
10320
  note: '月破、暗动、冲空三者各家取法不完全一致(有派别把日冲动爻也叫暗动,有派别只认旺相之爻暗动);本插件按「月建冲=月破、日冲静爻=暗动、日冲旬空=冲空」这一套给标签,并注明用了哪一支月建。',
9868
10321
  },
10322
+ {
10323
+ id: 'liuyao-quanggu-shuai',
10324
+ system: 'liuyao', dims: ['overall', 'work'],
10325
+ gist: '全卦逐爻看月建,才看得出气聚在哪一头。',
10326
+ detail: '月建管这一段时令的旺衰、日辰管当天的扶克,落到全部六爻逐爻判定:每爻都出月令五态(旺相休囚死)与日辰五关系,并标月破、暗动、冲空。世爻、应爻、动爻与旬空另标,五态分布能看出气是聚在世应还是散在旁爻——用神只是其中一处落点,不看全卦容易把一处当全局。',
10327
+ source: '《增删卜易》月建章、日辰章;《卜筮正宗》六爻旺衰论',
10328
+ status: 'variant',
10329
+ note: '各家对月破、暗动、冲空的取法不完全一致,是否给六爻折力重(分数)分歧更大;本插件只给状态标签与五态分布,不折分数。',
10330
+ },
9869
10331
  {
9870
10332
  id: 'liuyao-yingqi',
9871
10333
  system: 'liuyao', dims: ['overall', 'career'],
@@ -9931,7 +10393,7 @@ const KNOWLEDGE = [
9931
10393
  id: 'liuyao-jintui-yong',
9932
10394
  system: 'liuyao', dims: ['overall', 'work'],
9933
10395
  gist: '化进化退要看是用神还是忌神,还要叠月建日辰。',
9934
- detail: '进神退神本身只说明这一爻的向外趋势:用神化进为得力,忌神化进则为患加剧;用神化退是力衰,忌神化退反而是解脱。传统还要看它在本月令衰旺、有没有日辰生扶,空亡或受克时进者未必能进。所以本插件只标出化进/化退与爻位,力重与吉凶仍留给用神一节判断。',
10396
+ detail: '进神退神本身只说明这一爻的向外趋势:用神化进为得力,忌神化进则为患加剧;用神化退是力衰,忌神化退反而是解脱。传统还要看它在本月令衰旺、有没有日辰生扶,空亡或受克时进者未必能进。本插件把进退与爻位标出来,再按月建五态给一档力重——得令则进得动、退得慢,失令则进得虚、退得实,月破压过一切;只给档位,不折成加权分数,吉凶仍落在用神与忌神那一层判。',
9935
10397
  source: '《增删卜易》进神退神章、用神章',
9936
10398
  status: 'variant',
9937
10399
  note: '土之进退次序各家说法不完全一致,有以丑→辰→未→戌为进、也有只取辰戌丑未相对者;本插件按丑辰未戌成环处理,并用《增删卜易》卦例中「未土化戌土为进」「戌土化未土为退」对拍。',
@@ -10356,6 +10818,19 @@ function baziInterpret(r) {
10356
10818
  return `${jiaoLine}当前大运:${pick(cur, 'ganzhi', '—')}(${curSpan})。后续两步:${nextTwo}。\n近五年流年:${years || '—'}。\n读法:大运管"这十年的大环境",流年管"今年的具体天气";两者打架时,以能否动到用神为准。`;
10357
10819
  })(),
10358
10820
  },
10821
+ (() => {
10822
+ const hits = (r.liunian || []).filter((l) => l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
10823
+ return {
10824
+ key: 'suiyun', label: '岁运提示',
10825
+ text: hits.length
10826
+ ? hits.map((l) => {
10827
+ const stem = (l.suiyun.dayun || []).map((d) => `第${d.step}步${d.ganzhi}`).join('、');
10828
+ return `${l.year} ${l.ganzhi}(${stem || '未到起运'}):${l.suiyun.signals.map((s) => s.detail).join(';')}`;
10829
+ }).join('\n') +
10830
+ '\n读法:岁运并临、运年反吟与流年伏吟/反吟只说明干支层面重复或对冲,传统上当作「变动感较强」的信号;本插件不据此判吉凶,要落到具体年份,仍看该年是否动到用神。'
10831
+ : '近十年没有岁运并临、运年反吟或流年伏吟/反吟。「岁运并临主大凶」这类说法流传很广,本插件只把它当变动提示,没有命中就不作额外解读。',
10832
+ };
10833
+ })(),
10359
10834
  {
10360
10835
  key: 'checks', label: '交叉校验',
10361
10836
  text: (() => {
@@ -10685,6 +11160,38 @@ function ziweiInterpret(r) {
10685
11160
  }
10686
11161
  }
10687
11162
 
11163
+ {
11164
+ const fc = r.flyComposite;
11165
+ const word = (p) => `${p}${String(p).endsWith('宫') ? '' : '宫'}`;
11166
+ if (fc && Array.isArray(fc.sources) && fc.sources.length >= 2) {
11167
+ const hubs = fc.targets.filter((t) => t.count >= 2);
11168
+ const hubText = hubs.length
11169
+ ? hubs.slice(0, 5).map((t) => `${word(t.palace)}收了 ${t.incoming.map((x) => `${word(x.from)}化${x.type}${x.star}`).join('、')}`).join(';')
11170
+ : '四宫化出去之后没有落在同一宫,力是散开的';
11171
+ const readings = [];
11172
+ if (fc.groups.doubleJi.length) {
11173
+ readings.push(`${fc.groups.doubleJi.map((t) => word(t.palace)).join('、')}收到两个以上的忌——同一件事被反复牵动,传统看作那一宫的事务容易拖着不放`);
11174
+ }
11175
+ if (fc.groups.luJi.length) {
11176
+ readings.push(`${fc.groups.luJi.map((t) => word(t.palace)).join('、')}禄忌同宫:好处与在意的地方绑在一起,越想要的东西越容易反复`);
11177
+ }
11178
+ if (fc.groups.tripleHua.length) {
11179
+ readings.push(`${fc.groups.tripleHua.map((t) => word(t.palace)).join('、')}三化以上会宫:那一宫同时被几种力牵住,常常是这张盘最忙的地方`);
11180
+ }
11181
+ if (fc.groups.selfHua.length) {
11182
+ readings.push(`${fc.groups.selfHua.map((t) => word(t.palace)).join('、')}自化:宫干化出去又落回自己,表现为力气绕回原处`);
11183
+ }
11184
+ sections.push({
11185
+ key: 'flyComposite',
11186
+ label: '三方四正飞化',
11187
+ text: `上一节是命宫一条线往下追;这一节把命宫连同对宫与两个三合宫(${fc.sanfang.join('、')})一起飞:四宫各化禄权科忌,共 16 条飞化(星未上盘的只记星不记落宫),按落宫归堆。` +
11188
+ `${hubText}。` +
11189
+ `${readings.length ? `${readings.join(';')}。` : ''}` +
11190
+ `读法上,会在一处的化越多,那一宫被牵动的力道越集中;落单的飞化只说明方向,不单独取象。这一层同样只描会照与方向,不折吉凶,也没有叠大限与流年四化。`,
11191
+ });
11192
+ }
11193
+ }
11194
+
10688
11195
  if (r.flow) {
10689
11196
  const f = r.flow;
10690
11197
  const at = (p) => `${p.ganzhi || '—'}${p.palace ? `落${p.palace}` : ''}`;
@@ -10843,6 +11350,10 @@ function liuyaoInterpret(r, ask) {
10843
11350
  const tuiYao = (bd.jinTui || []).filter((x) => x.type === '退');
10844
11351
  if (jinYao.length) bodyText.push(`化进神(第${jinYao.map((x) => x.position).join('、')}爻:${jinYao.map((x) => `${x.from}→${x.to}`).join('、')}):变出的地支与本爻同五行、且顺着往前一步,传统读作「进」——这一爻代表的人或事有后劲,往前走比退回去顺。`);
10845
11352
  if (tuiYao.length) bodyText.push(`化退神(第${tuiYao.map((x) => x.position).join('、')}爻:${tuiYao.map((x) => `${x.from}→${x.to}`).join('、')}):变出的地支与本爻同五行、却倒着退一步,传统读作「退」——这一爻代表的人或事渐渐退气,别按原来的势头去推。`);
11353
+ const jp = r.jinTuiPower;
11354
+ if (jp && Array.isArray(jp.lines) && jp.lines.length) {
11355
+ bodyText.push(`进退的力重(放回月建日辰看):${jp.lines.map((l) => `第${l.position}爻${l.say}`).join(';')}。进退只管方向,走不走得动看时令托不托着;用神化进为喜,忌神化进反而是忌。`);
11356
+ }
10846
11357
  bodyText.push('这些标签只说明卦体的动势,不直接定吉凶:同一支六冲,近病问的是速愈、久病问的是凶险;同一爻化进,也得看用神旺不旺、有没有月建日辰托着。');
10847
11358
  sections.push({ key: 'body', label: '卦体关系', text: bodyText.join('\n') });
10848
11359
  }
@@ -10899,9 +11410,35 @@ function liuyaoInterpret(r, ask) {
10899
11410
  sections.push({ key: 'yongshen', label: '用神落位与卦体', text: useText.join('\n') });
10900
11411
  }
10901
11412
 
11413
+ {
11414
+ const hs = r.hexStrength;
11415
+ if (hs && Array.isArray(hs.lines) && hs.lines.length) {
11416
+ const head = hs.month && hs.month.branch
11417
+ ? `月建 ${hs.month.branch}(${hs.month.element})`
11418
+ : '没有月建(未给起卦时间)';
11419
+ const day = hs.day && hs.day.branch ? `日辰 ${hs.day.branch}(${hs.day.element})` : '日辰 —';
11420
+ const rows = hs.lines.map((l) => {
11421
+ const who = l.shi ? '(世)' : l.ying ? '(应)' : '';
11422
+ const flags = `${l.moving ? '·动' : ''}${l.kong ? '·旬空' : ''}`;
11423
+ return `${l.position}爻${l.liuqin ? l.liuqin : ''}${l.branch}${l.element}${who}${flags}:月建${l.monthState},${l.dayRelation}${l.chongTag ? `,${l.chongTag}` : ''}`;
11424
+ });
11425
+ const order = ['旺', '相', '休', '囚', '死'];
11426
+ const dist = order.filter((k) => hs.counts[k]).map((k) => `${k}${hs.counts[k]}`).join('、');
11427
+ const shi = hs.shi ? `世爻在${hs.shi.position}爻(月建${hs.shi.monthState},${hs.shi.dayRelation})` : '';
11428
+ const ying = hs.ying ? `应爻在${hs.ying.position}爻(月建${hs.ying.monthState},${hs.ying.dayRelation})` : '';
11429
+ sections.push({
11430
+ key: 'hexStrength',
11431
+ label: '全卦逐爻旺衰',
11432
+ text: `整张卦按${head}、${day}逐爻过一遍:${rows.join(';')}。五态分布:${dist}——${[shi, ying].filter(Boolean).join(',')}。` +
11433
+ `气旺的爻对应的事推进起来省力,气弱或遭月破的爻对应的事宜缓;这只是六爻全貌,用神那几爻的细节仍以上一节为准,两处不冲突时再看动爻把哪一头带动。` +
11434
+ `判定与用神旺衰同一套(月令五态、日辰五关系、月破/暗动/冲空),只报状态,不折成分数。`,
11435
+ });
11436
+ }
11437
+ }
11438
+
10902
11439
  sections.push({
10903
11440
  key: 'limits', label: '未纳入',
10904
- text: '本卦没有计算:全卦逐爻的月建旺衰(现在只算到用神那几爻)、化进化退叠上月建日辰之后的实际力重、用神两现时该取哪一爻的权重、伏神出伏的应期。这些会改变"旺不旺"的判断,所以这里只给取象与方向。',
11441
+ text: '本卦没有计算:用神两现时该取哪一爻的权重、伏神出伏的应期。化进/化退的力重只按月建五态给「得令/失令」这类档位,不折成加权分数,也不叠动变连环;这些都会改变"旺不旺"的判断,所以这里只给取象与方向。',
10905
11442
  });
10906
11443
 
10907
11444
  const dimensions = [
@@ -11592,7 +12129,54 @@ function followup(r, question) {
11592
12129
  return { question: q, ...result };
11593
12130
  }
11594
12131
 
11595
- return { followup };
12132
+ const SYSTEM_LABEL = {
12133
+ bazi: '八字(四柱)',
12134
+ ziwei: '紫微斗数',
12135
+ liuyao: '六爻',
12136
+ meihua: '梅花易数',
12137
+ daliuren: '大六壬',
12138
+ qimen: '奇门遁甲',
12139
+ tarot: '塔罗',
12140
+ hepan: '合盘',
12141
+ chenggu: '称骨',
12142
+ almanac: '黄历',
12143
+ zeri: '择日',
12144
+ star: '星座',
12145
+ numerology: '生命数字',
12146
+ mbti: '十六型人格',
12147
+ trend: '人生 K 线',
12148
+ fengshui: '风水',
12149
+ name: '起名',
12150
+ convert: '公农历互转',
12151
+ };
12152
+
12153
+ function askPrompt(r, opts = {}) {
12154
+ const label = SYSTEM_LABEL[r && r.system] || (r && r.system) || '未知体系';
12155
+ const asked = String((opts && opts.question) || '').trim();
12156
+ const question = asked || '请先总览这张盘,指出最值得注意的一两处,并说明各自的依据。';
12157
+ const limit = Number.isFinite(opts && opts.maxChars) && opts.maxChars >= 400 ? opts.maxChars : 3000;
12158
+ const raw = String((r && r.summary) || '').trim();
12159
+ const truncated = raw.length > limit;
12160
+ const summary = truncated ? `${raw.slice(0, limit)}…(摘要已截断)` : raw;
12161
+
12162
+ const lines = [
12163
+ '【命理追问】下面这张盘是命理插件刚排好的,请就着它回答我的问题;不用重新排盘,盘面字段已经够用。',
12164
+ `体系:${label}`,
12165
+ `所问:${question}`,
12166
+ '',
12167
+ '——盘面摘要——',
12168
+ summary || '(这张盘没有摘要字段,请先让我重新排一次。)',
12169
+ '',
12170
+ '——回答要求——',
12171
+ '1) 尽量引用到具体的柱、宫或爻,再说判断,别只给结论;',
12172
+ '2) 盘面里没有依据的地方直接说「这盘里看不出来」,不要补外部假设;',
12173
+ '3) 用概率和倾向的说法,别给绝对化的断言,也别把传统说法当成事实。',
12174
+ ];
12175
+ const text = lines.join('\n');
12176
+ return { text, question, system: (r && r.system) || '', label, chars: text.length, truncated };
12177
+ }
12178
+
12179
+ return { followup, askPrompt };
11596
12180
  })();
11597
12181
 
11598
12182
  __mods.crosscheck = (function () {
@@ -11882,7 +12466,7 @@ return { matchEvent, rectifyBirth, RECTIFY_KINDS };
11882
12466
 
11883
12467
  __mods.index = (function () {
11884
12468
  const { buildBazi } = __mods.bazi;
11885
- const { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength } = __mods.liuyao;
12469
+ const { buildLiuyao, tossLines, bodyLabels, yongshenOf, yongshenStrength, hexagramStrength, jinTuiPower } = __mods.liuyao;
11886
12470
  const { buildZiwei, BRIGHTNESS, flowOf, asOfParts } = __mods.ziwei;
11887
12471
  const { drawTarot } = __mods.tarot;
11888
12472
  const { buildMeihua } = __mods.meihua;
@@ -11894,7 +12478,7 @@ const { geocodeAddress, resolveAddress } = __mods.geo;
11894
12478
  const { toJDN, ganzhiName, dayGanzhiIndex } = __mods.calendar;
11895
12479
  const { randomOf, isSeed } = __mods.random;
11896
12480
  const { interpret, DIRECTIONS, DIRECTION_DEFAULT } = __mods.interpret;
11897
- const { followup } = __mods.followup;
12481
+ const { followup, askPrompt } = __mods.followup;
11898
12482
  const { crossCheck, crossText } = __mods.crosscheck;
11899
12483
  const { pastSignals, verifyFit } = __mods.verify;
11900
12484
  const { rectifyBirth } = __mods.rectify;
@@ -12007,6 +12591,13 @@ function baziText(r) {
12007
12591
  `大运:${r.dayun.rule}(${r.dayun.direction}),${r.dayun.startAge.years}岁${r.dayun.startAge.months}个月起运${jiao}`,
12008
12592
  ` ${dayun}`,
12009
12593
  `流年:${r.liunian.slice(0, 10).map((l) => `${l.year}${l.ganzhi}(${l.stemTenGod})${l.luck ? `·${l.luck.tier}` : ''}`).join(' ')}`,
12594
+ (() => {
12595
+ const hits = r.liunian.filter((l) => l.suiyun && l.suiyun.signals && l.suiyun.signals.length);
12596
+ if (!hits.length) return '岁运提示:近十年无岁运并临、运年反吟或流年伏吟/反吟(只标干支,不判吉凶)';
12597
+ const label = (s) => `${s.type}${s.type.startsWith('流年') && s.target ? `(${s.target})` : ''}`;
12598
+ return '岁运提示(只标干支变动,不判吉凶):' + hits
12599
+ .map((l) => `${l.year}${l.ganzhi} ${l.suiyun.signals.map(label).join('/')}`).join(';');
12600
+ })(),
12010
12601
  '(格局、用神、行运吉凶为扶抑+简化调候的量化提示,非断语。)',
12011
12602
  ].join('\n') + near;
12012
12603
  }
@@ -12044,6 +12635,17 @@ function ziweiText(r) {
12044
12635
  const text = ['禄', '权', '科', '忌'].map(one).filter(Boolean).join(' ');
12045
12636
  return text ? `【命宫飞星】${text}` : '';
12046
12637
  })();
12638
+ const flyCompositeLine = (() => {
12639
+ const fc = r.flyComposite;
12640
+ if (!fc || !Array.isArray(fc.sources) || !fc.sources.length) return '';
12641
+ const groups = fc.labels.length ? fc.labels.join(' · ') : '无同宫会聚';
12642
+ const detail = ['禄', '权', '科', '忌'].map((t) => {
12643
+ const list = ((fc.byType && fc.byType[t]) || []).filter((x) => !x.self);
12644
+ const tos = [...new Set(list.map((x) => String(x.to).replace(/宫$/, '')))];
12645
+ return tos.length ? `${t}→${tos.join('/')}` : '';
12646
+ }).filter(Boolean).join(' ');
12647
+ return `【三方四正飞化】${fc.sanfang.join('·')}:${groups}${detail ? ` ${detail}` : ''}`;
12648
+ })();
12047
12649
  return [
12048
12650
  `【农历】${lunarText}(月首 ${r.lunar.monthStart}) 时支:${r.hourBranch} 年干支:${r.yearGanzhi}`,
12049
12651
  `【时刻】${r.solarTime ? `${r.solarTime.civilText} → 真太阳时 ${r.solarTime.trueSolarText}${r.input.geo ? '(' + r.input.geo.note + ')' : ''}` : ''}`,
@@ -12053,6 +12655,7 @@ function ziweiText(r) {
12053
12655
  `【昌曲】文昌${r.changqu.wenchang.branch}(${r.changqu.wenchang.palace}${r.changqu.wenchang.mutagen ? '·化' + r.changqu.wenchang.mutagen : ''}) 文曲${r.changqu.wenqu.branch}(${r.changqu.wenqu.palace}${r.changqu.wenqu.mutagen ? '·化' + r.changqu.wenqu.mutagen : ''})`,
12054
12656
  `   ${r.changqu.relation} 入命三方:${r.changqu.inSoulSanfang ? '是' : '否'} 吸收面:${r.changqu.absorb.label}(${r.changqu.absorb.reason})`,
12055
12657
  flyLine,
12658
+ flyCompositeLine,
12056
12659
  '十二宫:',
12057
12660
  ...rows,
12058
12661
  '大限(按起运年龄排序,前 8 限,宫干四化落宫):',
@@ -12060,7 +12663,7 @@ function ziweiText(r) {
12060
12663
  '流年(前 6 年,完整 12 年在结构化数据里):',
12061
12664
  ...liunian,
12062
12665
  `【格局】${patterns}`,
12063
- r.patternSummary ? `【结构统计】吉格 ${r.patternSummary.good.length}、忌格 ${r.patternSummary.bad.length};${r.patternSummary.note}` : '',
12666
+ r.patternSummary ? `【结构统计】吉格 ${r.patternSummary.good.length}、中性 ${r.patternSummary.neutral.length}、忌格 ${r.patternSummary.bad.length};${r.patternSummary.note}` : '',
12064
12667
  r.lunar.note ? `注:${r.lunar.note}` : '',
12065
12668
  ].filter(Boolean).join('\n');
12066
12669
  }
@@ -12205,6 +12808,17 @@ function liuyaoText(r) {
12205
12808
  } else {
12206
12809
  out.push('用神:未取(未指明所问之事;用「询问方向」指定后即按六亲取用)');
12207
12810
  }
12811
+ const hexSt = r.hexStrength;
12812
+ if (hexSt && Array.isArray(hexSt.labels) && hexSt.labels.length) {
12813
+ const m = hexSt.month && hexSt.month.branch
12814
+ ? `月建 ${hexSt.month.branch}(${hexSt.month.element}) `
12815
+ : '无月建(未给起卦时间) ';
12816
+ out.push(`全卦旺衰:${m}${hexSt.labels.join(' · ')}`);
12817
+ }
12818
+ const jt = r.jinTuiPower;
12819
+ if (jt && Array.isArray(jt.lines) && jt.lines.length) {
12820
+ out.push(`进退力重:${jt.lines.map((l) => `${l.position}爻${l.from}→${l.to}${l.type}·${l.verdict}`).join(' · ')}`);
12821
+ }
12208
12822
  return out.filter(Boolean).join('\n');
12209
12823
  }
12210
12824
 
@@ -12485,17 +13099,21 @@ function computeRaw(args = {}) {
12485
13099
  throw fail('E_LINES', e && e.message ? e.message : e);
12486
13100
  }
12487
13101
  const ug = yongshenOf(result, { direction: args.direction, gender: args.gender });
12488
- if (ug.liuqin) {
12489
- result.useGod = ug;
12490
- let monthBranch = null;
12491
- if (stamp) {
12492
- try {
12493
- const snap = buildSnapshot({ year: stamp.year, month: stamp.month, day: stamp.day, hour: stamp.hour, minute: stamp.minute });
12494
- monthBranch = (snap.monthGanZhi || '')[1] || null;
12495
- } catch {
12496
- monthBranch = null;
12497
- }
13102
+ if (ug.liuqin) result.useGod = ug;
13103
+ let monthBranch = null;
13104
+ if (stamp) {
13105
+ try {
13106
+ const snap = buildSnapshot({ year: stamp.year, month: stamp.month, day: stamp.day, hour: stamp.hour, minute: stamp.minute });
13107
+ monthBranch = (snap.monthGanZhi || '')[1] || null;
13108
+ } catch {
13109
+ monthBranch = null;
12498
13110
  }
13111
+ }
13112
+ const hexStrength = hexagramStrength(result, { monthBranch, dayBranch: result.dayGanZhi.branch });
13113
+ if (hexStrength) result.hexStrength = hexStrength;
13114
+ const jtPower = jinTuiPower(result, { monthBranch, dayBranch: result.dayGanZhi.branch });
13115
+ if (jtPower) result.jinTuiPower = jtPower;
13116
+ if (ug.liuqin) {
12499
13117
  const strength = yongshenStrength(result, ug, { monthBranch, dayBranch: result.dayGanZhi.branch });
12500
13118
  if (strength) result.useGodStrength = strength;
12501
13119
  }
@@ -12881,11 +13499,12 @@ function renderText(result) {
12881
13499
  return parts.filter(Boolean).join('\n\n');
12882
13500
  }
12883
13501
 
12884
- return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAddress, resolveAddress, buildBazi, buildLiuyao, tossLines, followup, pastSignals, verifyFit, rectifyBirth, crossCheck, crossText, buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildConvert, buildTrend, buildFengshui, buildName, parseDateTime, compute, interpretText, renderText };
13502
+ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAddress, resolveAddress, buildBazi, buildLiuyao, tossLines, followup, askPrompt, pastSignals, verifyFit, rectifyBirth, crossCheck, crossText, buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildConvert, buildTrend, buildFengshui, buildName, parseDateTime, compute, interpretText, renderText };
12885
13503
  })();
12886
13504
 
12887
13505
  const Engine = __mods.index;
12888
13506
  window.Engine = Engine;
13507
+ window.__MP_VER = '0.38.2';
12889
13508
  (function () {
12890
13509
  const NEED_ASK = { liuyao: true, meihua: true, daliuren: true, tarot: true };
12891
13510
  const needAsk = (kind) => !!NEED_ASK[kind];
@@ -12916,25 +13535,28 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
12916
13535
 
12917
13536
  (function () {
12918
13537
  const CSS = `
12919
- .pp,.mp-it{--ink:#1b1b1b;--ink2:#3f3a33;--muted:#6f6a60;--label:#8a8378;--faint:#a9a296;
13538
+ .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;
12920
13539
  --line:#c8c1b2;--dimline:#b9b2a4;--hair:#e9e2d3;
12921
13540
  --bg:#f6f1e6;--card:#fffdf8;--band:#fbf7ee;--hover:#f3ede1;--hover2:#ece5d6;
12922
13541
  --tri:#f3e8cd;--soul:#fae4dc;--sel:#fceee7;--link:#f9ece0;
12923
13542
  --red:#b3261e;--red-rgb:179,38,30;--lu:#1f6f3f;--lu-rgb:31,111,63;
12924
13543
  --quan:#8a5a00;--quan-rgb:138,90,0;--ke:#1f4e79;--ke-rgb:31,78,121;--ji:#b3261e;
12925
13544
  --br-good:#4c7a5c;--br-bad:#9c5b56;--on-accent:#fff;--wx-metal:#8a6b1f;
12926
- --tg-bi:#4a6f8a;--tg-shi:#4c7a5c;--tg-cai:#8a6b1f;--tg-guan:#9c5b56;--tg-yin:#6d5f83}
13545
+ --tg-bi:#4a6f8a;--tg-shi:#4c7a5c;--tg-cai:#8a6b1f;--tg-guan:#9c5b56;--tg-yin:#6d5f83;
13546
+ --ink3:#8a8378;--fx:"Kaiti SC",KaiTi,"STKaiti","Songti SC",serif;--sans:system-ui,-apple-system,"Microsoft YaHei",sans-serif;--kai:"Kaiti SC",KaiTi,"STKaiti",serif;
13547
+ --shadow:0 1px 2px rgba(35,28,18,.05),0 18px 40px -30px rgba(35,28,18,.42)}
12927
13548
  .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;
12928
- container-type:inline-size}
13549
+ border-radius:12px;overflow:hidden;box-shadow:var(--shadow);container-type:inline-size}
12929
13550
  @media screen and (prefers-color-scheme:dark){
12930
- .pp,.mp-it{--ink:#eae5d9;--ink2:#d5cfc2;--muted:#aca598;--label:#a49c8e;--faint:#928b7e;
13551
+ .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;
12931
13552
  --line:#514a3e;--dimline:#5f5849;--hair:#2f2b23;
12932
13553
  --bg:#12110c;--card:#211f18;--band:#282419;--hover:#2e2a1f;--hover2:#383322;
12933
13554
  --tri:#332913;--soul:#3a231c;--sel:#43271f;--link:#33241c;
12934
13555
  --red:#f08569;--red-rgb:240,133,105;--lu:#74c795;--lu-rgb:116,199,149;
12935
13556
  --quan:#e2a95c;--quan-rgb:226,169,92;--ke:#86bcea;--ke-rgb:134,188,234;--ji:#f08569;
12936
13557
  --br-good:#79c79a;--br-bad:#e09a8c;--on-accent:#14130f;--wx-metal:#dcb273;
12937
- --tg-bi:#93b7cf;--tg-shi:#8fbb9d;--tg-cai:#d3ae62;--tg-guan:#d98d84;--tg-yin:#b4a4d0}
13558
+ --tg-bi:#93b7cf;--tg-shi:#8fbb9d;--tg-cai:#d3ae62;--tg-guan:#d98d84;--tg-yin:#b4a4d0;
13559
+ --ink3:#8b8478;--shadow:0 1px 2px rgba(0,0,0,.5),0 18px 40px -30px rgba(0,0,0,.9)}
12938
13560
  }
12939
13561
  .pp *{box-sizing:border-box}
12940
13562
  .pp-grid{display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:repeat(4,minmax(150px,auto));gap:0;border:1px solid var(--line)}
@@ -13024,6 +13646,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13024
13646
 
13025
13647
  .pp-cell{cursor:pointer;transition:background .14s ease,box-shadow .14s ease;outline:none}
13026
13648
  .pp-cell:hover{background:var(--hover)}
13649
+ .pp-cell:focus-visible{box-shadow:inset 0 0 0 2px var(--red)}
13027
13650
  .pp-cell.pp-sel{background:var(--sel);box-shadow:inset 0 0 0 2px rgba(var(--red-rgb),.5)}
13028
13651
  .pp-cell.pp-link{background:var(--link);box-shadow:inset 0 0 0 1.5px rgba(var(--red-rgb),.22)}
13029
13652
  .pp-detail{display:none;border-top:1px solid var(--line);background:var(--card);padding:11px 14px 13px;font-family:system-ui,sans-serif}
@@ -13079,7 +13702,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13079
13702
  .mp-trig{display:inline-block;width:14px;height:10px;vertical-align:-1px;margin-right:4px;color:currentColor}
13080
13703
 
13081
13704
  .mp-hy-wrap{display:flex;gap:22px;flex-wrap:wrap;align-items:flex-start}
13082
- .mp-hy-box{border:1px solid var(--line);background:var(--card);padding:12px 16px 14px;min-width:250px}
13705
+ .mp-hy-box{border:1px solid var(--line);border-radius:10px;background:var(--card);padding:12px 16px 14px;min-width:250px}
13083
13706
  .mp-hy-h{display:flex;align-items:baseline;gap:8px;margin-bottom:10px;font-family:system-ui,sans-serif}
13084
13707
  .mp-hy-h b{color:var(--red);font-size:14px}
13085
13708
  .mp-hy-h span{color:var(--muted);font-size:12px}
@@ -13098,7 +13721,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13098
13721
  .mp-mh{padding:16px}
13099
13722
  .mp-mh-head{font-size:13px;color:var(--muted);margin-bottom:12px}
13100
13723
  .mp-mh-main{display:flex;gap:18px;flex-wrap:wrap;align-items:flex-start}
13101
- .mp-hexbox{border:1px solid var(--line);background:var(--card);padding:12px 15px 13px}
13724
+ .mp-hexbox{border:1px solid var(--line);border-radius:10px;background:var(--card);padding:12px 15px 13px}
13102
13725
  .mp-hexbox.hb-mini{min-width:118px}
13103
13726
  .mp-hexbox .hb-h{display:flex;justify-content:space-between;gap:10px;font-size:12px;letter-spacing:.14em;color:var(--muted);font-family:system-ui,sans-serif;margin-bottom:5px}
13104
13727
  .mp-hexbox .hb-n{font-size:17px;font-weight:700;color:var(--red);margin-bottom:8px;letter-spacing:.04em}
@@ -13268,7 +13891,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13268
13891
  .mp-tt{padding:16px}
13269
13892
  .mp-tt-head{font-size:12.5px;color:var(--muted);margin-bottom:12px;letter-spacing:.04em}
13270
13893
  .mp-tt-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(172px,1fr));gap:10px}
13271
- .mp-tt-card{border:1px solid var(--line);background:var(--card);padding:13px 14px;display:flex;flex-direction:column;gap:7px;min-height:168px}
13894
+ .mp-tt-card{border:1px solid var(--line);border-radius:10px;background:var(--card);padding:13px 14px;display:flex;flex-direction:column;gap:7px;min-height:168px}
13272
13895
  .mp-tt-card.tt-rev{background:var(--band);border-style:dashed}
13273
13896
  .mp-tt-pos{font-size:12px;color:var(--muted);letter-spacing:.14em}
13274
13897
  .mp-tt-name{font-size:19px;font-weight:700;color:var(--red);letter-spacing:.04em}
@@ -13288,11 +13911,12 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13288
13911
  .mp-tt-ta{width:100%;border:1px solid var(--line);background:var(--card);color:var(--ink);padding:8px;font-size:13px;font-family:inherit;line-height:1.7;resize:vertical;outline:none}
13289
13912
  .mp-tt-ta:focus{border-color:var(--red)}
13290
13913
 
13291
- .mp-it{margin-top:14px;border:1px solid var(--line);background:var(--bg);color:var(--ink);
13914
+ .mp-it{margin-top:14px;border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:12px;overflow:hidden;box-shadow:var(--shadow);
13292
13915
  font-family:"Songti SC","SimSun","Microsoft YaHei",serif;font-size:13px;line-height:1.75}
13293
13916
  .mp-it *{box-sizing:border-box}
13294
13917
  .mp-it-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap;width:100%;padding:12px 16px;border:none;border-bottom:1px solid var(--line);background:var(--card);color:inherit;font:inherit;font-family:inherit;text-align:left;cursor:pointer;-webkit-appearance:none;appearance:none}
13295
13918
  .mp-it-head:hover{background:var(--band)}
13919
+ .mp-it-head:focus-visible{outline:2px solid var(--red);outline-offset:-3px}
13296
13920
  .mp-it-arrow{flex:none;font-size:12px;line-height:1;color:var(--red);transition:transform .16s ease}
13297
13921
  .mp-it.folded .mp-it-arrow{transform:rotate(-90deg)}
13298
13922
  .mp-it.folded .mp-it-wrap{display:none}
@@ -13367,7 +13991,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13367
13991
  .mp-tables{border-top:1px solid var(--line);padding:12px 16px 4px;background:var(--card)}
13368
13992
  .mp-table-wrap{margin-bottom:12px}
13369
13993
  .mp-table-h{font-size:12px;font-weight:700;letter-spacing:.1em;color:var(--lu);margin-bottom:5px;font-family:system-ui,sans-serif}
13370
- .mp-table-scroll{overflow-x:auto;border:1px solid var(--line);border-radius:6px}
13994
+ .mp-table-scroll{overflow-x:auto;border:1px solid var(--line);border-radius:8px}
13371
13995
  .mp-table{width:100%;border-collapse:collapse;font-size:12px;line-height:1.6;color:var(--ink2);font-family:system-ui,sans-serif}
13372
13996
  .mp-table th,.mp-table td{padding:5px 9px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line);white-space:nowrap}
13373
13997
  .mp-table th{background:var(--band);color:var(--label);font-weight:600;font-size:11px;letter-spacing:.06em}
@@ -13433,15 +14057,17 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13433
14057
  .mp-qt-y b.first{color:var(--ji);border-bottom:2px solid var(--ji);padding-bottom:1px}
13434
14058
  .mp-qt-q{margin-top:6px;font-size:12.5px;line-height:1.8;color:var(--ink2);font-family:var(--fx)}
13435
14059
  .mp-qt-q span{display:block;font-size:11px;color:var(--ink3);font-family:var(--sans);margin-top:2px}
13436
- .mp-cl{margin-top:10px;padding-top:9px;border-top:1px dashed var(--line)}
13437
- .mp-cl-h{display:flex;gap:8px;align-items:baseline;font-size:12.5px;margin-bottom:6px}
13438
- .mp-cl-h b{color:var(--ink);letter-spacing:.02em}
14060
+ .mp-cl{margin-top:12px;padding:10px 12px 8px;border-left:2px solid var(--quan);border-radius:0 8px 8px 0;
14061
+ background:color-mix(in srgb,var(--band) 72%,transparent)}
14062
+ .mp-cl-h{display:flex;gap:8px;align-items:baseline;font-size:12.5px;margin-bottom:7px}
14063
+ .mp-cl-h b{color:var(--ink);letter-spacing:.06em}
13439
14064
  .mp-cl-h span{color:var(--ink3);font-size:11.5px}
13440
- .mp-cl-e{padding:6px 0;border-top:1px dashed var(--hair)}
14065
+ .mp-cl-e{padding:8px 0;border-top:1px dashed var(--hair)}
13441
14066
  .mp-cl-e:first-of-type{border-top:0;padding-top:0}
13442
- .mp-cl-t b{font-family:var(--fx);font-size:13px;color:var(--lu);font-weight:600}
13443
- .mp-cl-q{font-family:var(--fx);font-size:13.5px;line-height:1.95;color:var(--ink);margin-top:2px;letter-spacing:.02em}
13444
- .mp-cl-p{font-size:12px;line-height:1.85;color:var(--ink2);margin-top:2px}
14067
+ .mp-cl-t b{font-family:var(--fx);font-size:13px;color:var(--lu);font-weight:600;letter-spacing:.03em}
14068
+ .mp-cl-q{font-family:var(--fx);font-size:14px;line-height:2;color:var(--ink);margin-top:3px;letter-spacing:.02em;
14069
+ padding-left:1.05em;text-indent:-1.05em}
14070
+ .mp-cl-p{font-size:12px;line-height:1.9;color:var(--ink2);margin-top:3px}
13445
14071
  @media print{.mp-cl{break-inside:avoid}}
13446
14072
  @media print{.mp-cli{break-inside:avoid}}
13447
14073
  .mp-nm{margin:12px 16px;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:12px 14px;font-family:system-ui,sans-serif}
@@ -13471,30 +14097,48 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13471
14097
  .mp-nm-c em{font-style:normal;font-size:11.5px;line-height:1.7;color:var(--muted);font-family:var(--sans)}
13472
14098
  @media print{.mp-nm{break-inside:avoid}}
13473
14099
  .mp-quick-pre{margin:0;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.9;color:var(--ink2)}
13474
- .mp-mb{margin:12px 16px;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:12px 14px;font-family:system-ui,sans-serif}
13475
- .mp-mb-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;border-bottom:1px solid var(--hair);padding-bottom:8px;margin-bottom:9px}
13476
- .mp-mb-h b{font-family:var(--fx);font-size:20px;letter-spacing:1px;color:var(--ink)}
13477
- .mp-mb-h i{font-style:normal;font-size:12px;color:var(--muted)}
13478
- .mp-mb-dims{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 14px;margin-bottom:9px}
13479
- .mp-mb-dim{display:grid;grid-template-columns:28px minmax(0,1fr) 28px;gap:6px;align-items:center;font-size:11.5px;color:var(--muted)}
13480
- .mp-mb-dim b{font-size:13px;font-weight:600;color:var(--muted);font-family:var(--fx)}
13481
- .mp-mb-dim b.on{color:var(--ink)}
13482
- .mp-mb-bar{position:relative;height:10px;border-radius:5px;background:var(--hair);overflow:hidden}
13483
- .mp-mb-bar em{position:absolute;top:0;bottom:0;display:block;background:var(--line)}
13484
- .mp-mb-bar em.on{background:var(--good)}
13485
- .mp-mb-dim i{font-style:normal;grid-column:1/-1;font-size:11px;color:var(--muted);line-height:1.6}
13486
- .mp-mb-sec{margin:7px 0 0;font-size:12.5px;line-height:1.85;color:var(--ink2)}
13487
- .mp-mb-sec em{font-style:normal;color:var(--lu);margin-right:6px}
13488
- .mp-mb-two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:8px}
13489
- .mp-mb-two>div{border:1px dashed var(--hair);border-radius:6px;padding:7px 9px;font-size:12.5px;line-height:1.8;color:var(--ink2)}
13490
- .mp-mb-two b{display:block;font-size:11.5px;color:var(--lu);font-weight:600;margin-bottom:3px}
13491
- .mp-mb-fn{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0 0}
13492
- .mp-mb-fn span{font-size:11.5px;border:1px solid var(--line);border-radius:999px;padding:1px 8px;color:var(--ink2)}
13493
- .mp-mb-note{margin-top:9px;font-size:11px;line-height:1.7;color:var(--muted);border-top:1px solid var(--hair);padding-top:7px}
13494
- .mp-mb-paper{margin:12px 16px;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:12px 14px;font-family:system-ui,sans-serif}
13495
- .mp-mb-paper ol{margin:6px 0 0;padding-left:20px;font-size:12.5px;line-height:1.9;color:var(--ink2)}
13496
- .mp-mb-paper .mp-mb-note{margin-top:8px}
13497
- @media (max-width:640px){.mp-mb-dims,.mp-mb-two{grid-template-columns:1fr}}
14100
+ .mp-mb{margin:12px 16px;border:1px solid var(--line);border-radius:12px;background:var(--card);box-shadow:var(--shadow);overflow:hidden;font-family:var(--sans)}
14101
+ .mp-mb-h{display:flex;flex-direction:row;align-items:center;gap:14px;flex-wrap:wrap;padding:15px 18px 14px;
14102
+ background:linear-gradient(135deg,var(--sel),var(--band) 64%,var(--card));border-bottom:1px solid var(--hair)}
14103
+ .mp-mb-code{font-family:var(--fx);font-size:32px;font-weight:700;line-height:1;letter-spacing:.12em;color:var(--ink)}
14104
+ .mp-mb-meta{display:flex;flex-direction:column;gap:3px;min-width:0}
14105
+ .mp-mb-meta b{font-size:14.5px;font-weight:600;color:var(--ink);letter-spacing:.04em}
14106
+ .mp-mb-meta i{font-style:normal;font-size:12px;line-height:1.5;color:var(--muted)}
14107
+ .mp-mb-count{margin-left:auto;padding:3px 10px;border:1px solid var(--line);border-radius:999px;background:var(--card);color:var(--muted);font-size:11.5px;white-space:nowrap}
14108
+ .mp-mb-body{padding:14px 18px 16px}
14109
+ .mp-mb-dims{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
14110
+ .mp-mb-dim{border:1px solid var(--hair);border-radius:10px;background:var(--band);padding:9px 11px 10px}
14111
+ .mp-mb-dh{display:flex;align-items:baseline;gap:6px;font-size:11.5px;color:var(--muted)}
14112
+ .mp-mb-dh b{font-family:var(--fx);font-size:17px;font-weight:600;line-height:1.1;letter-spacing:.08em;color:var(--faint)}
14113
+ .mp-mb-dh b.on{color:var(--ink)}
14114
+ .mp-mb-dh em{font-style:normal;margin:0 auto;font-size:11.5px;letter-spacing:.06em}
14115
+ .mp-mb-bar{position:relative;height:7px;border-radius:4px;background:var(--hair);margin:7px 0 6px;overflow:hidden}
14116
+ .mp-mb-bar::after{content:"";position:absolute;left:50%;top:-1px;bottom:-1px;width:1px;background:color-mix(in srgb,var(--line) 70%,transparent)}
14117
+ .mp-mb-bar em{position:absolute;top:0;bottom:0;background:linear-gradient(90deg,var(--quan),color-mix(in srgb,var(--quan) 55%,var(--card)));border-radius:4px}
14118
+ .mp-mb-bar.l em{left:0}
14119
+ .mp-mb-bar.r em{right:0;background:linear-gradient(270deg,var(--quan),color-mix(in srgb,var(--quan) 55%,var(--card)))}
14120
+ .mp-mb-df{font-size:11.5px;color:var(--ink2);font-variant-numeric:tabular-nums}
14121
+ .mp-mb-ds{margin-top:2px;font-size:11.5px;line-height:1.7;color:var(--muted)}
14122
+ .mp-mb-sec{margin-top:11px;font-size:12.5px;line-height:1.85;color:var(--ink2)}
14123
+ .mp-mb-sec em{display:inline-block;margin-right:7px;padding:0 7px;border:1px solid var(--hair);border-radius:5px;background:var(--band);color:var(--muted);font-size:11px;font-style:normal;letter-spacing:.08em}
14124
+ .mp-mb-two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:11px}
14125
+ .mp-mb-two>div{border:1px solid var(--hair);border-left:3px solid var(--br-good);border-radius:8px;background:var(--card);padding:8px 10px;font-size:12.5px;line-height:1.8;color:var(--ink2)}
14126
+ .mp-mb-two>div+div{border-left-color:var(--br-bad)}
14127
+ .mp-mb-two b{display:block;margin-bottom:2px;font-size:11.5px;font-weight:600;letter-spacing:.08em;color:var(--br-good)}
14128
+ .mp-mb-two>div+div b{color:var(--br-bad)}
14129
+ .mp-mb-fn{display:flex;flex-wrap:wrap;gap:6px;margin-top:7px}
14130
+ .mp-mb-fn span{font-size:11.5px;border:1px solid var(--line);border-radius:999px;background:var(--card);padding:2px 9px;color:var(--ink2)}
14131
+ .mp-mb-fn span.lead{border-color:var(--quan);background:color-mix(in srgb,var(--quan) 9%,var(--card));color:var(--quan);font-weight:600}
14132
+ .mp-mb-fn span b{font-weight:600;margin-right:4px}
14133
+ .mp-mb-note{margin:12px 18px 14px;padding-top:10px;font-size:11px;line-height:1.7;color:var(--muted);border-top:1px solid var(--hair)}
14134
+ .mp-mb-paper{margin:12px 16px;border:1px solid var(--line);border-radius:12px;background:var(--card);box-shadow:var(--shadow);overflow:hidden;font-family:var(--sans)}
14135
+ .mp-mb-paper ol{counter-reset:q;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px 20px;
14136
+ margin:0;padding:12px 18px 14px;list-style:none;font-size:12.5px;line-height:1.7;color:var(--ink2)}
14137
+ .mp-mb-paper li{counter-increment:q;position:relative;padding:0 0 6px 26px;border-bottom:1px dashed var(--hair)}
14138
+ .mp-mb-paper li::before{content:counter(q);position:absolute;left:0;top:1px;display:flex;align-items:center;justify-content:center;
14139
+ width:19px;height:19px;border:1px solid var(--hair);border-radius:50%;background:var(--band);color:var(--muted);font-size:10.5px;font-variant-numeric:tabular-nums}
14140
+ .mp-mb-paper .mp-mb-note{margin:0 18px 14px}
14141
+ @media (max-width:640px){.mp-mb-dims,.mp-mb-two,.mp-mb-paper ol{grid-template-columns:1fr}.mp-mb-code{font-size:26px}.mp-mb-body{padding:12px 14px 14px}.mp-mb-h{padding:13px 14px 12px}}
13498
14142
  @media print{.mp-mb{break-inside:avoid}}
13499
14143
  .mp-al{margin:12px 16px;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:12px 14px;font-family:system-ui,sans-serif}
13500
14144
  .mp-al-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;border-bottom:1px solid var(--hair);padding-bottom:8px;margin-bottom:9px}
@@ -13889,6 +14533,14 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13889
14533
  }).join('');
13890
14534
  return chips ? `<div class="pp-chips">${chips}</div>` : '';
13891
14535
  })() : '';
14536
+ const flyCompositeHTML = r.flyComposite && Array.isArray(r.flyComposite.labels) && r.flyComposite.labels.length
14537
+ ? `<div class="pp-chips">${r.flyComposite.labels.map((l) => {
14538
+ const cls = /忌/.test(l) ? 'warn' : /自化/.test(l) ? '' : 'ok';
14539
+ const palaceOf = (x) => String(x).replace(/^.*?会/, '').replace(/^禄忌同宫/, '').replace(/自化$/, '');
14540
+ const hit = (r.flyComposite.targets || []).find((t) => t.palace === palaceOf(l));
14541
+ const tip = hit ? hit.incoming.map((x) => `${x.from}化${x.type}${x.star}`).join('、') : r.flyComposite.note;
14542
+ return `<span class="pp-chip ${cls}" title="${esc(tip)}">三方四正 ${esc(l)}</span>`;
14543
+ }).join('')}</div>` : '';
13892
14544
 
13893
14545
  const fl = r.flow;
13894
14546
  const flowHTML = fl ? (() => {
@@ -13918,7 +14570,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
13918
14570
  ${body('ben', soulIdx, 'ben')}
13919
14571
  ${body('da', dxIdx, 'da')}
13920
14572
  ${body('ln', lnIdx, 'ln')}
13921
- </div>${flowHTML}${chips}${flyHTML}${pats}${dxs}${lnHit}`;
14573
+ </div>${flowHTML}${chips}${flyHTML}${flyCompositeHTML}${pats}${dxs}${lnHit}`;
13922
14574
  };
13923
14575
 
13924
14576
  function starLine(list) {
@@ -14100,12 +14752,29 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
14100
14752
  return `<span class="pp-chip ${cls}">${esc(l)}</span>`;
14101
14753
  }).join('')}</div>`
14102
14754
  : '';
14755
+ const jp = r.jinTuiPower && Array.isArray(r.jinTuiPower.labels) ? r.jinTuiPower : null;
14756
+ const jinRow = jp && jp.labels.length
14757
+ ? `<div class="pp-chips">${jp.labels.map((l) => {
14758
+ const cls = /后劲足|退得缓/.test(l) ? 'ok' : /有限|进得虚|退得实|月破|未定/.test(l) ? 'warn' : '';
14759
+ return `<span class="pp-chip ${cls}">${esc(l)}</span>`;
14760
+ }).join('')}</div>`
14761
+ : '';
14762
+ const hs = r.hexStrength && Array.isArray(r.hexStrength.labels) ? r.hexStrength : null;
14763
+ const hexRow = hs && hs.labels.length
14764
+ ? `<div class="pp-chips">${hs.labels.map((l) => {
14765
+ const cls = /死|囚|月破|冲空/.test(l) ? 'warn'
14766
+ : /旺|相/.test(l) ? 'ok' : '';
14767
+ return `<span class="pp-chip ${cls}">${esc(l)}</span>`;
14768
+ }).join('')}</div>`
14769
+ : '';
14103
14770
  return `<div class="pp" style="padding:14px">
14104
14771
  <div class="mp-mh-head">起卦 ${r.cast.notation.join('、')}(自下而上) 日辰 ${r.dayGanZhi.name}</div>
14105
14772
  <div class="mp-hy-wrap">${ben}${bian}</div>
14106
14773
  ${bodyRow}
14107
14774
  ${useRow}
14108
14775
  ${strengthRow}
14776
+ ${jinRow}
14777
+ ${hexRow}
14109
14778
  </div>`;
14110
14779
  };
14111
14780
 
@@ -14794,8 +15463,13 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
14794
15463
  }).join('');
14795
15464
  const ln = (r.liunian && r.liunian.length) ? `<div class="pp-chips">${r.liunian.slice(0, 8).map((l) => {
14796
15465
  const lk = l.luck || null;
14797
- return `<span class="pp-chip ${lk ? tierCls(lk.tier) : ''}" title="${esc(lk ? `${lk.tier} ${lk.score}|${(lk.reasons || []).join(';')}` : '')}">${l.year} ${l.ganzhi}${lk ? ` ${lk.tier}` : ''}</span>`;
15466
+ const sy = (l.suiyun && Array.isArray(l.suiyun.signals)) ? l.suiyun.signals : [];
15467
+ const syTip = sy.length ? `|${sy.map((x) => x.detail).join(';')}` : '';
15468
+ return `<span class="pp-chip ${lk ? tierCls(lk.tier) : ''}" title="${esc(lk ? `${lk.tier} ${lk.score}|${(lk.reasons || []).join(';')}${syTip}` : syTip.replace(/^|/, ''))}">${l.year} ${l.ganzhi}${lk ? ` ${lk.tier}` : ''}</span>`;
14798
15469
  }).join('')}</div>` : '';
15470
+ const sy = (r.liunian || []).filter((l) => l.suiyun && Array.isArray(l.suiyun.signals) && l.suiyun.signals.length);
15471
+ const syRow = sy.length ? `<div class="pp-tl pp-dim">岁运提示(只标干支变动,不判吉凶)</div><div class="pp-chips">${sy.map((l) =>
15472
+ `<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>` : '';
14799
15473
  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>` : '';
14800
15474
  const ssGroups = (r.shensha && r.shensha.groups) || [];
14801
15475
  const ssx = ssGroups.length ? `
@@ -14815,14 +15489,14 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
14815
15489
  ${(Array.isArray(r.classics) && r.classics.length) ? `<section class="mp-cl" aria-label="古籍原文">
14816
15490
  <div class="mp-cl-h"><b>古籍原文</b><span>照录通行本,释义为白话自撰</span></div>
14817
15491
  ${r.classics.map((e) => `<div class="mp-cl-e">
14818
- <div class="mp-cl-t"><b>${esc((e.book === 'ziping' ? '子平真诠' : '滴天髓') + '·' + e.chapter)}</b></div>
15492
+ <div class="mp-cl-t"><b>${esc(({ ziping: '子平真诠', ditian: '滴天髓', sanming: '三命通会' }[e.book] || e.book) + '·' + e.chapter)}</b></div>
14819
15493
  <div class="mp-cl-q">「${esc(e.quote)}」</div>
14820
15494
  <div class="mp-cl-p">${esc(e.plain)}</div>
14821
15495
  </div>`).join('')}
14822
15496
  </section>` : ''}
14823
15497
  ${sect('五行与十神', `<div class="pp-chips">${wx}</div>${gods}`)}
14824
15498
  ${sect('大运', `${pp.curveHTML(r)}<div class="pp-chips">${dy}</div>`)}
14825
- ${ln ? sect('流年', ln) : ''}
15499
+ ${ln ? sect('流年', ln + syRow) : ''}
14826
15500
  ${r.palaces ? sect('胎元与命身宫', `<div class="pp-chips">
14827
15501
  <span class="pp-chip" title="${r.palaces.taiyuan.note}">胎元 ${r.palaces.taiyuan.ganzhi}</span>
14828
15502
  <span class="pp-chip" title="${r.palaces.minggong.note}">命宫 ${r.palaces.minggong.ganzhi}</span>
@@ -14959,7 +15633,10 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
14959
15633
  if (r.needAnswers && Array.isArray(r.questions)) {
14960
15634
  const items = r.questions.map((q) => `<li>${esc(q.text)}</li>`).join('');
14961
15635
  return `<section class="mp-mb-paper" aria-label="十六型人格问卷">
14962
- <div class="mp-mb-h"><b>十六型人格问卷</b><i>${r.total} 题 · 1 完全不像 / 5 很像我</i></div>
15636
+ <div class="mp-mb-h">
15637
+ <span class="mp-mb-meta"><b>十六型人格问卷</b><i>${r.total} 题 · 每题 1 完全不像 / 5 很像我</i></span>
15638
+ <span class="mp-mb-count">填完分数再看结果</span>
15639
+ </div>
14963
15640
  <ol>${items}</ol>
14964
15641
  <div class="mp-mb-note">${esc(r.note || '')}</div>
14965
15642
  </section>`;
@@ -14968,32 +15645,35 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
14968
15645
  const pct = Math.round((d.pct || 0) * 100);
14969
15646
  const leftOn = d.picked === d.left;
14970
15647
  const width = Math.max(2, pct);
14971
- const bar = leftOn
14972
- ? `<em class="on" style="left:0;width:${width}%"></em>`
14973
- : `<em class="on" style="right:0;width:${width}%"></em>`;
14974
15648
  const say = leftOn ? d.leftSay : d.rightSay;
14975
15649
  return `<div class="mp-mb-dim">
14976
- <b class="${leftOn ? 'on' : ''}">${esc(d.left)}</b>
14977
- <div class="mp-mb-bar">${bar}</div>
14978
- <b class="${leftOn ? '' : 'on'}">${esc(d.right)}</b>
14979
- <i>${esc(d.name)} · 取 ${esc(d.picked)}(${esc(d.strength)} ${pct}%)· ${esc(say)}</i>
15650
+ <div class="mp-mb-dh"><b class="${leftOn ? 'on' : ''}">${esc(d.left)}</b><em>${esc(d.name)}</em><b class="${leftOn ? '' : 'on'}">${esc(d.right)}</b></div>
15651
+ <div class="mp-mb-bar ${leftOn ? 'l' : 'r'}"><em style="width:${width}%"></em></div>
15652
+ <div class="mp-mb-df">取 ${esc(d.picked)} · ${esc(d.strength)} ${pct}%</div>
15653
+ <div class="mp-mb-ds">${esc(say)}</div>
14980
15654
  </div>`;
14981
15655
  }).join('');
14982
15656
  const p = r.profile || {};
14983
- const stack = (p.stack || []).map((f) => `<span>${esc(f.code)} ${esc(f.say)}</span>`).join('');
15657
+ const stack = (p.stack || []).map((f, i) => `<span class="${i === 0 ? 'lead' : ''}"><b>${esc(f.code)}</b>${esc(f.say)}</span>`).join('');
14984
15658
  return `<section class="mp-mb" aria-label="十六型人格结果">
14985
- <div class="mp-mb-h"><b>${esc(r.type || '')}</b><i>${esc(p.title || '')} · ${esc(p.keywords || '')} 已答 ${r.answered || 0}/${r.total || 0} 题</i></div>
14986
- <div class="mp-mb-dims">${dims}</div>
14987
- <div class="mp-mb-sec"><em>一句</em>${esc(p.oneLine || '')}</div>
14988
- <div class="mp-mb-two">
14989
- <div><b>优势</b>${(p.strengths || []).map((s) => esc(s)).join(';')}</div>
14990
- <div><b>留意</b>${(p.blindspots || []).map((s) => esc(s)).join(';')}</div>
15659
+ <div class="mp-mb-h">
15660
+ <span class="mp-mb-code">${esc(r.type || '')}</span>
15661
+ <span class="mp-mb-meta"><b>${esc(p.title || '')}</b><i>${esc(p.keywords || '')}</i></span>
15662
+ <span class="mp-mb-count">已答 ${r.answered || 0}/${r.total || 0} 题</span>
15663
+ </div>
15664
+ <div class="mp-mb-body">
15665
+ <div class="mp-mb-dims">${dims}</div>
15666
+ <div class="mp-mb-sec"><em>一句</em>${esc(p.oneLine || '')}</div>
15667
+ <div class="mp-mb-two">
15668
+ <div><b>优势</b>${(p.strengths || []).map((s) => esc(s)).join(';')}</div>
15669
+ <div><b>留意</b>${(p.blindspots || []).map((s) => esc(s)).join(';')}</div>
15670
+ </div>
15671
+ ${stack ? `<div class="mp-mb-sec"><em>认知功能</em>主导 ${esc((p.stack[0] || {}).code || '')} → 依次展开</div><div class="mp-mb-fn">${stack}</div>` : ''}
15672
+ <div class="mp-mb-sec"><em>工作</em>${esc(p.work || '')}</div>
15673
+ <div class="mp-mb-sec"><em>关系</em>${esc(p.love || '')}</div>
15674
+ <div class="mp-mb-sec"><em>学习</em>${esc(p.study || '')}</div>
15675
+ <div class="mp-mb-sec"><em>课题</em>${esc(p.growth || '')}</div>
14991
15676
  </div>
14992
- ${stack ? `<div class="mp-mb-sec"><em>认知功能</em>主导 ${esc((p.stack[0] || {}).code || '')} → 依次展开</div><div class="mp-mb-fn">${stack}</div>` : ''}
14993
- <div class="mp-mb-sec"><em>工作</em>${esc(p.work || '')}</div>
14994
- <div class="mp-mb-sec"><em>关系</em>${esc(p.love || '')}</div>
14995
- <div class="mp-mb-sec"><em>学习</em>${esc(p.study || '')}</div>
14996
- <div class="mp-mb-sec"><em>课题</em>${esc(p.growth || '')}</div>
14997
15677
  <div class="mp-mb-note">${esc(r.note || '')}</div>
14998
15678
  </section>`;
14999
15679
  };
@@ -15151,7 +15831,8 @@ const PP = window.__PP;
15151
15831
  const CSS = `
15152
15832
  .mp-wrap{height:100%;overflow:auto;padding:20px 24px 40px;color:var(--dsw-alias-label-primary);font-size:14px}
15153
15833
  .mp-wrap>*{max-width:940px;margin-left:auto;margin-right:auto}
15154
- .mp-panel{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:14px;padding:16px 18px 14px;margin-bottom:14px}
15834
+ .mp-panel{background:linear-gradient(180deg,var(--dsw-alias-bg-layer-2),color-mix(in srgb,var(--dsw-alias-bg-layer-2) 88%,var(--dsw-alias-bg-layer-1)));border:1px solid var(--dsw-alias-border-l1);border-radius:16px;padding:18px 20px 16px;margin-bottom:16px;
15835
+ box-shadow:0 1px 2px color-mix(in srgb,var(--dsw-alias-label-primary) 5%,transparent),0 16px 34px -30px color-mix(in srgb,var(--dsw-alias-label-primary) 45%,transparent)}
15155
15836
  .mp-row{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px 14px;align-items:end}
15156
15837
  .mp-lbl{display:block;height:18px;line-height:18px;font-size:12.5px;color:var(--dsw-alias-label-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
15157
15838
  .mp-checks{display:flex;gap:20px;flex-wrap:wrap;margin-top:13px;font-size:12.5px;color:var(--dsw-alias-label-secondary)}
@@ -15159,17 +15840,50 @@ const CSS = `
15159
15840
  .mp-askblock{margin-top:14px;display:flex;flex-direction:column;gap:5px}
15160
15841
  .mp-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:14px}
15161
15842
  .mp-tabs{display:inline-flex;gap:4px;margin-bottom:14px;flex-wrap:wrap;padding:4px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:11px;width:100%;max-width:940px}
15162
- .mp-tab{padding:7px 15px;border-radius:8px;border:none;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:13.5px;transition:background .12s,color .12s}
15163
- .mp-tab:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}
15164
- .mp-tab.on{background:var(--dsw-alias-brand-primary);color:#fff}
15843
+ .mp-tab{padding:7px 15px;border-radius:9px;border:none;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:13.5px;transition:background .14s,color .14s,box-shadow .14s}
15844
+ .mp-tab:hover{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}
15845
+ .mp-tab.on{background:var(--dsw-alias-brand-primary);color:#fff;box-shadow:0 8px 18px -12px var(--dsw-alias-brand-primary)}
15165
15846
  .mp-field{display:flex;flex-direction:column;gap:5px;font-size:13px;color:var(--dsw-alias-label-secondary);min-width:0}
15166
- .mp-input{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:7px 11px;font-size:14px;outline:none;width:100%;min-width:0;height:36px}
15167
- .mp-input:focus{border-color:var(--dsw-alias-brand-primary)}
15168
- .mp-textarea{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:8px 11px;font-size:14px;outline:none;width:100%;min-width:0;resize:vertical;line-height:1.5;font-family:inherit}
15169
- .mp-textarea:focus{border-color:var(--dsw-alias-brand-primary)}
15847
+ .mp-input{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);border-radius:9px;padding:7px 11px;font-size:14px;outline:none;width:100%;min-width:0;height:36px;
15848
+ transition:border-color .14s,box-shadow .14s,background .14s}
15849
+ .mp-input:hover{border-color:color-mix(in srgb,var(--dsw-alias-border-l1) 45%,var(--dsw-alias-brand-primary))}
15850
+ .mp-input:focus{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 18%,transparent)}
15851
+ .mp-textarea{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);border-radius:9px;padding:8px 11px;font-size:14px;outline:none;width:100%;min-width:0;resize:vertical;line-height:1.5;font-family:inherit;
15852
+ transition:border-color .14s,box-shadow .14s}
15853
+ .mp-textarea:hover{border-color:color-mix(in srgb,var(--dsw-alias-border-l1) 45%,var(--dsw-alias-brand-primary))}
15854
+ .mp-textarea:focus{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 18%,transparent)}
15170
15855
  .mp-textarea.invalid{border-color:var(--dsw-alias-state-error-primary)}
15171
- .mp-btn{padding:8px 22px;border-radius:8px;border:none;background:var(--dsw-alias-brand-primary);color:#fff;cursor:pointer;font-size:14px;font-weight:600;min-width:132px}
15172
- .mp-btn.ghost{background:transparent;border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary)}
15856
+ .mp-quiz-wrap{grid-column:1 / -1;display:flex;flex-direction:column;gap:6px}
15857
+ .mp-quiz-top{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
15858
+ .mp-quiz-bar{flex:1 1 160px;min-width:110px;height:6px;border:1px solid var(--dsw-alias-border-l1);border-radius:999px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}
15859
+ .mp-quiz-bar i{display:block;height:100%;border-radius:999px;background:var(--dsw-alias-brand-primary);transition:width .18s ease}
15860
+ .mp-quiz-count{font-size:12px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap}
15861
+ .mp-quiz-list{display:flex;flex-direction:column;gap:8px;margin-top:8px}
15862
+ .mp-quiz-q{padding:10px 12px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}
15863
+ .mp-quiz-qt{display:flex;gap:8px;align-items:baseline;font-size:13px;line-height:1.6;color:var(--dsw-alias-label-primary)}
15864
+ .mp-quiz-qt b{flex:none;padding-top:1px;font:600 11px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;letter-spacing:.04em;color:var(--dsw-alias-brand-primary)}
15865
+ .mp-quiz-opts{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:6px;margin-top:8px}
15866
+ .mp-quiz-o{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:6px 4px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:center;
15867
+ transition:border-color .14s,background .14s,color .14s}
15868
+ .mp-quiz-o input{position:absolute;inset:0;margin:0;opacity:0;cursor:pointer}
15869
+ .mp-quiz-o b{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums}
15870
+ .mp-quiz-o span{font-size:11px;line-height:1.3}
15871
+ .mp-quiz-o:hover{border-color:var(--dsw-alias-brand-primary)}
15872
+ .mp-quiz-o.on{border-color:transparent;background:var(--dsw-alias-brand-primary)}
15873
+ .mp-quiz-o.on b,.mp-quiz-o.on span{color:#fff}
15874
+ .mp-quiz-o:focus-within{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
15875
+ .mp-quiz-nav{display:flex;align-items:center;gap:8px;margin-top:10px;flex-wrap:wrap}
15876
+ .mp-quiz-btn{padding:6px 14px;border-radius:8px;border:1px solid var(--dsw-alias-border-l1);background:transparent;color:var(--dsw-alias-label-primary);font-family:inherit;font-size:12.5px;cursor:pointer;min-height:32px}
15877
+ .mp-quiz-btn:hover:not(:disabled){border-color:var(--dsw-alias-brand-primary)}
15878
+ .mp-quiz-btn:disabled{opacity:.42;cursor:not-allowed}
15879
+ .mp-quiz-btn.primary{border-color:transparent;background:var(--dsw-alias-brand-primary);color:#fff;font-weight:600}
15880
+ .mp-quiz-hint{margin-left:auto;font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.6}
15881
+ .mp-btn{padding:8px 22px;border-radius:9px;border:none;background:var(--dsw-alias-brand-primary);color:#fff;cursor:pointer;font-size:14px;font-weight:600;min-width:132px;
15882
+ box-shadow:0 10px 20px -14px var(--dsw-alias-brand-primary);transition:transform .14s,filter .14s,box-shadow .14s}
15883
+ .mp-btn:hover{filter:brightness(1.06);transform:translateY(-1px)}
15884
+ .mp-btn:active{transform:none;filter:brightness(.98)}
15885
+ .mp-btn.ghost{background:transparent;border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);box-shadow:none}
15886
+ .mp-btn.ghost:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);filter:none}
15173
15887
  .mp-seg{display:flex;width:100%;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;overflow:hidden;height:36px}
15174
15888
  .mp-seg button{flex:1;padding:0 8px;background:transparent;border:none;color:var(--dsw-alias-label-secondary);font-size:13px;cursor:pointer}
15175
15889
  .mp-seg button.on{background:var(--dsw-alias-brand-primary);color:#fff}
@@ -15195,7 +15909,8 @@ const CSS = `
15195
15909
  .mp-hist-item b{font-weight:600}
15196
15910
  .mp-hist-item span{color:var(--dsw-alias-label-secondary)}
15197
15911
  .mp-hist-item i{font-style:normal;color:var(--dsw-alias-label-secondary);opacity:.7;font-size:11px}.mp-hint{font-size:12.5px;color:var(--dsw-alias-label-secondary);line-height:1.7;margin:0 0 14px;padding-left:2px}
15198
- .mp-card{margin-top:18px;padding:16px 18px;border-radius:14px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1)}
15912
+ .mp-card{margin-top:18px;padding:16px 18px;border-radius:16px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);
15913
+ box-shadow:0 1px 2px color-mix(in srgb,var(--dsw-alias-label-primary) 5%,transparent),0 18px 40px -32px color-mix(in srgb,var(--dsw-alias-label-primary) 42%,transparent)}
15199
15914
  .mp-flat{margin-top:16px;padding:0;border:none;background:transparent;border-radius:0}
15200
15915
  .mp-dim{color:var(--dsw-alias-label-secondary)}
15201
15916
  .mp-err{margin-top:16px;padding:9px 14px;border-radius:8px;border-left:3px solid var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent);color:var(--dsw-alias-state-error-primary);font-size:13px}
@@ -15233,6 +15948,11 @@ const CSS = `
15233
15948
  .mp-tab{padding:9px 4px;font-size:13.5px;text-align:center}
15234
15949
  .mp-input,.mp-textarea{padding:10px 12px;font-size:16px;min-height:44px;height:auto}
15235
15950
  .mp-textarea{min-height:76px}
15951
+ .mp-quiz-q{padding:9px 10px}
15952
+ .mp-quiz-o{padding:8px 3px;min-height:50px}
15953
+ .mp-quiz-o span{font-size:10px}
15954
+ .mp-quiz-btn{flex:1 1 auto;min-height:44px}
15955
+ .mp-quiz-hint{flex-basis:100%;margin-left:0}
15236
15956
  .mp-actions{margin-top:12px}
15237
15957
  .mp-btn{width:100%;padding:12px;font-size:15px;min-height:46px;min-width:0}
15238
15958
  .mp-hint,.mp-auto,.mp-cast{font-size:12.5px}
@@ -15246,10 +15966,81 @@ const CSS = `
15246
15966
  .mp-wrap{padding:12px 10px 32px}
15247
15967
  .mp-tab{font-size:13px;padding:8px 2px}
15248
15968
  }
15969
+ :where(.mp-btn,.mp-tab,.mp-chip,.mp-hist-item,.mp-hist-clear,.mp-qmopt,.seg button):focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
15970
+ @media (prefers-reduced-motion:reduce){
15971
+ *,*::before,*::after{transition-duration:.001ms!important;animation-duration:.001ms!important}
15972
+ }
15249
15973
  `;
15250
15974
 
15251
15975
  const { useState, useEffect } = React;
15252
15976
 
15977
+ const MBTI_PER_PAGE = 4;
15978
+ const MBTI_MIN = 12;
15979
+ let MBTI_INFO = null;
15980
+ function mbtiInfo() {
15981
+ if (MBTI_INFO) return MBTI_INFO;
15982
+ let p = null;
15983
+ try { p = Engine.compute({ system: 'mbti' }); } catch (e) { p = null; }
15984
+ const questions = (p && p.questions) || [];
15985
+ MBTI_INFO = { total: (p && p.total) || questions.length, questions, scale: (p && p.scale) || {} };
15986
+ return MBTI_INFO;
15987
+ }
15988
+
15989
+ function MbtiQuiz(props) {
15990
+ const { answers, page, onPick, onPage, onSubmit, onClear } = props;
15991
+ const info = mbtiInfo();
15992
+ const pages = Math.max(1, Math.ceil(info.total / MBTI_PER_PAGE));
15993
+ const start = page * MBTI_PER_PAGE;
15994
+ const slice = info.questions.slice(start, start + MBTI_PER_PAGE);
15995
+ const answered = answers.filter((v) => v != null).length;
15996
+ const pct = info.total ? Math.round((answered / info.total) * 100) : 0;
15997
+ const last = page >= pages - 1;
15998
+ const ready = answered >= MBTI_MIN;
15999
+ return h('div', { className: 'mp-quiz' },
16000
+ h('div', { className: 'mp-quiz-top' },
16001
+ h('div', { className: 'mp-quiz-bar', 'aria-hidden': 'true' }, h('i', { style: { width: pct + '%' } })),
16002
+ h('span', { className: 'mp-quiz-count' }, `第 ${page + 1} / ${pages} 页 · 已答 ${answered} / ${info.total}`)),
16003
+ h('div', { className: 'mp-quiz-list' }, slice.map((q, k) => {
16004
+ const i = start + k;
16005
+ return h('div', { key: q.id || i, className: 'mp-quiz-q' },
16006
+ h('div', { className: 'mp-quiz-qt' }, h('b', null, String(i + 1).padStart(2, '0')), h('span', null, q.text)),
16007
+ h('div', { className: 'mp-quiz-opts', role: 'radiogroup', 'aria-label': `第 ${i + 1} 题` },
16008
+ [1, 2, 3, 4, 5].map((n) => h('label', { key: n, className: 'mp-quiz-o' + (answers[i] === n ? ' on' : '') },
16009
+ h('input', {
16010
+ type: 'radio', name: `mpmbq${i}`, value: String(n), checked: answers[i] === n,
16011
+ onChange: () => onPick(i, n),
16012
+ }),
16013
+ h('b', null, String(n)),
16014
+ h('span', null, info.scale[n] || '')))));
16015
+ })),
16016
+ h('div', { className: 'mp-quiz-nav' },
16017
+ h('button', { className: 'mp-quiz-btn', disabled: page <= 0, onClick: () => onPage(-1) }, '上一页'),
16018
+ last ? null : h('button', { className: 'mp-quiz-btn', onClick: () => onPage(1) }, '下一页'),
16019
+ h('button', { className: 'mp-quiz-btn primary', disabled: !ready, onClick: onSubmit }, '看结果'),
16020
+ h('button', { className: 'mp-quiz-btn', onClick: onClear }, '清空'),
16021
+ h('span', { className: 'mp-quiz-hint' }, ready
16022
+ ? `已够 ${MBTI_MIN} 题,可以看结果;答完 ${info.total} 题会更准。`
16023
+ : `答满 ${MBTI_MIN} 题可先看结果(还差 ${MBTI_MIN - answered} 题)。`)),
16024
+ );
16025
+ }
16026
+
16027
+ class PanelGuard extends (React.Component || Object) {
16028
+ constructor(props) { super(props); this.state = { err: null }; }
16029
+ static getDerivedStateFromError(err) { return { err }; }
16030
+ componentDidCatch(err, info) { console.error('[metaphysics] 面板渲染异常', err, info); }
16031
+ render() {
16032
+ if (!this.state.err) return this.props.children;
16033
+ const e = this.state.err;
16034
+ const stack = String((e && e.stack) || '').split('\n').slice(0, 14).join('\n');
16035
+ return h('div', { className: 'mp-wrap' }, h('div', { className: 'mp-panel' },
16036
+ h('div', { className: 'mp-lbl' }, '命理面板渲染出错——把下面这段发给我'),
16037
+ h('pre', { style: { whiteSpace: 'pre-wrap', fontSize: 12, lineHeight: 1.5, maxHeight: 320, overflow: 'auto', margin: '8px 0' } },
16038
+ String((e && e.message) || e) + '\n\n' + stack),
16039
+ h('div', { className: 'mp-lbl' }, '环境:__PP ' + (typeof PP) + ' · Engine ' + (typeof window.Engine) + ' · React ' + (typeof React) + ' · 版本 ' + (typeof PP === 'object' && PP && (window.__MP_VER || '?'))),
16040
+ ));
16041
+ }
16042
+ }
16043
+
15253
16044
  function PanelIcon(props) {
15254
16045
  const size = props && props.size ? props.size : 20;
15255
16046
  const active = props && props.active;
@@ -15280,7 +16071,7 @@ const TABS = [
15280
16071
  { k: 'xiaoliuren', label: '小六壬', timeLabel: '起课时刻', hint: '小六壬用农历月/日/时掐指,也可用三个随机数起课。' },
15281
16072
  { k: 'star', label: '星座', timeLabel: '', hint: '星座按公历日期给出太阳星座与当日基调;同日同星座结果固定。' },
15282
16073
  { k: 'numerology', label: '生命数字', timeLabel: '', hint: '生命数字按公历生日归约出生命路径数、生日数与态度数。' },
15283
- { k: 'mbti', label: '人格', timeLabel: '', hint: '十六型人格是一份 MBTI 风格问卷:24 题按 1–5 打分,四维度各取一侧;只作自我参照,不是官方量表。' },
16074
+ { k: 'mbti', label: '人格', timeLabel: '', hint: '十六型人格是 24 道自我报告题的 MBTI 风格测算:4 题一页,按 1–5 打分,答满 12 题即可看结果;只作自我参照,不是官方量表。' },
15284
16075
  { k: 'trend', label: '人生K线', timeLabel: '出生时刻', hint: '人生 K 线按大运与流年十神给逐年运势分,画成走势图。' },
15285
16076
  { k: 'fengshui', label: '风水', timeLabel: '', hint: '风水看八宅命卦、大游年四吉四凶与流年九宫飞星;也可按坐向看门主灶倾向。' },
15286
16077
  { k: 'name', label: '起名', timeLabel: '', hint: '起名按八字喜用匹配字库五行,可评估现有名字或生成候选名。' },
@@ -15308,6 +16099,85 @@ const kindOf = (t) => TIME_KIND[t] || 'none';
15308
16099
  const Core = window.__MPCore || {};
15309
16100
  const needAsk = Core.needAsk || ((t) => false);
15310
16101
 
16102
+ const COMPOSER = { ref: null, seats: [], keys: '', injectType: '', spec: '', errs: [], regs: 0, mounted: 0 };
16103
+ const LAST = { r: null, q: '' };
16104
+
16105
+ function bridgeDiagText() {
16106
+ const bits = [];
16107
+ bits.push('inputActions ' + (COMPOSER.ref ? '已拿到' : '未拿到'));
16108
+ bits.push('渲染 ' + COMPOSER.mounted + ' 次');
16109
+ bits.push('注册 ' + COMPOSER.regs + ' 次');
16110
+ bits.push('inject ' + (COMPOSER.injectType || '?'));
16111
+ bits.push('槽位 ' + (COMPOSER.spec || '?'));
16112
+ if (COMPOSER.errs.length) bits.push('错误:' + COMPOSER.errs.join(' / '));
16113
+ return '桥自检:' + bits.join(';');
16114
+ }
16115
+
16116
+ function noteRegError(e) {
16117
+ if (COMPOSER.errs.length >= 3) return;
16118
+ const s = e && e.message ? String(e.message) : String(e);
16119
+ COMPOSER.errs.push(s.slice(0, 90));
16120
+ }
16121
+ function bridgeActions(props) {
16122
+ if (!props) return null;
16123
+ const a = props.inputActions;
16124
+ if (a && typeof a.setDraft === 'function' && typeof a.submit === 'function') return a;
16125
+ const b = props.actions && props.actions.input;
16126
+ if (b && typeof b.setDraft === 'function' && typeof b.submit === 'function') return b;
16127
+ return null;
16128
+ }
16129
+
16130
+ function useBridge(props, name) {
16131
+ const actions = bridgeActions(props);
16132
+ useEffect(() => {
16133
+ if (COMPOSER.seats.indexOf(name) < 0) COMPOSER.seats.push(name);
16134
+ COMPOSER.keys = props ? Object.keys(props).join(',') : '';
16135
+ if (actions) {
16136
+ COMPOSER.ref = actions;
16137
+ try { window.__MPComposer = actions; } catch (e) { /* 非浏览器环境忽略 */ }
16138
+ }
16139
+ return () => { if (COMPOSER.ref === actions) COMPOSER.ref = null; };
16140
+ }, [actions, name]);
16141
+ return actions;
16142
+ }
16143
+
16144
+ function askTextForLast() {
16145
+ const r = LAST.r;
16146
+ if (!r) return '';
16147
+ try {
16148
+ const p = Engine.askPrompt ? Engine.askPrompt(r, { question: LAST.q }) : null;
16149
+ return (p && p.text) || String(r.summary || '');
16150
+ } catch (e) { return String(r.summary || ''); }
16151
+ }
16152
+
16153
+ function ComposerBridge(props) {
16154
+ COMPOSER.mounted += 1;
16155
+ const actions = useBridge(props, 'input.right');
16156
+ if (!actions) {
16157
+ return h('span', { className: 'mp-chip warn', title: '命理:已占住输入框槽位,但没拿到 inputActions;把这句话告诉作者' }, '命理·未就绪');
16158
+ }
16159
+ return h('button', {
16160
+ type: 'button', className: 'mp-chip ok',
16161
+ title: LAST.r ? '把上一张盘的摘要和问题发进对话窗' : '先在侧栏「命理」里排一张盘',
16162
+ onClick: () => { const t = askTextForLast(); if (t) sendToComposer(t); },
16163
+ }, '命理·问');
16164
+ }
16165
+
16166
+ function ComposerProbe(props) {
16167
+ COMPOSER.mounted += 1;
16168
+ useBridge(props, 'composer.dock');
16169
+ return null;
16170
+ }
16171
+
16172
+ function sendToComposer(text) {
16173
+ let ia = COMPOSER.ref;
16174
+ if (!ia) { try { ia = window.__MPComposer || null; } catch (e) { ia = null; } }
16175
+ if (!ia || typeof ia.setDraft !== 'function' || typeof ia.submit !== 'function') return false;
16176
+ ia.setDraft(text);
16177
+ ia.submit();
16178
+ return true;
16179
+ }
16180
+
15311
16181
  function App() {
15312
16182
  const [tab, setTab] = useState('ziwei');
15313
16183
  const [date, setDate] = useState('1990-05-12');
@@ -15325,6 +16195,7 @@ function App() {
15325
16195
  const [qLateZi, setQLateZi] = useState('same-day');
15326
16196
  const [crossOn, setCrossOn] = useState(false);
15327
16197
  const [askText, setAskText] = useState('');
16198
+ const [chatNote, setChatNote] = useState('');
15328
16199
  const [fx, setFx] = useState(null);
15329
16200
  const [pastMarks, setPastMarks] = useState({});
15330
16201
  const [fit, setFit] = useState(null);
@@ -15338,7 +16209,8 @@ function App() {
15338
16209
  const [leapMonth, setLeapMonth] = useState(false);
15339
16210
  const [xlrMode, setXlrMode] = useState('time');
15340
16211
  const [num1, setNum1] = useState(''); const [num2, setNum2] = useState(''); const [num3, setNum3] = useState('');
15341
- const [answers, setAnswers] = useState('');
16212
+ const [mbAns, setMbAns] = useState(() => new Array(mbtiInfo().total).fill(null));
16213
+ const [mbPage, setMbPage] = useState(0);
15342
16214
  const [toAge, setToAge] = useState('60');
15343
16215
  const [fsMode, setFsMode] = useState('natal');
15344
16216
  const [sitting, setSitting] = useState('子');
@@ -15350,6 +16222,9 @@ function App() {
15350
16222
  const [onlineGeo, setOnlineGeo] = useState(false);
15351
16223
  const [lines, setLines] = useState('');
15352
16224
  const [result, setResult] = useState(null);
16225
+ useEffect(() => {
16226
+ if (result && result.r) { LAST.r = result.r; LAST.q = askText.trim(); }
16227
+ }, [result, askText]);
15353
16228
  const [hist, setHist] = useState(() => loadHistory());
15354
16229
  const [replayArgs, setReplayArgs] = useState(null);
15355
16230
  const [error, setError] = useState('');
@@ -15371,7 +16246,7 @@ function App() {
15371
16246
  || tab === 'chenggu' || tab === 'trend' || tab === 'fengshui' || tab === 'name';
15372
16247
  const timeKind = kindOf(tab);
15373
16248
  const usesTimeInput = !(tab === 'meihua' && mhMode === 'number');
15374
- const showDate = tab !== 'tarot' && usesTimeInput;
16249
+ const showDate = tab !== 'tarot' && tab !== 'mbti' && usesTimeInput;
15375
16250
  const showTime = showDate && timeKind !== 'date';
15376
16251
  const showDT = showTime;
15377
16252
 
@@ -15381,6 +16256,7 @@ function App() {
15381
16256
  const dt = override ? override : { date, time };
15382
16257
  const genderV = over && over.gender ? over.gender : gender;
15383
16258
  const addressV = over && typeof over.address === 'string' ? over.address : address;
16259
+ setNotice(''); // 上一次的提示(如「先答满 12 题」)不该跟着新结果一起留下
15384
16260
  try {
15385
16261
  const datetime = `${dt.date}T${dt.time}`;
15386
16262
  const args = { system: k };
@@ -15427,7 +16303,8 @@ function App() {
15427
16303
  }
15428
16304
  if (k === 'mbti') {
15429
16305
  delete args.datetime;
15430
- const nums = String(answers).split(/[^0-9]+/).filter((x) => x !== '').map((x) => Number(x));
16306
+ const nums = mbAns.slice();
16307
+ while (nums.length && nums[nums.length - 1] == null) nums.pop();
15431
16308
  if (nums.length) args.answers = nums;
15432
16309
  }
15433
16310
  if (k === 'trend') { args.gender = genderV; args.toAge = Number(toAge) || 60; args.trueSolar = trueSolar; args.lateZi = lateZi; }
@@ -15528,7 +16405,7 @@ function App() {
15528
16405
  setResult({ kind: k, r, it, at: dt, timed, trend });
15529
16406
  setFx(null); setFit(null); setPastMarks({}); setRectRes(null);
15530
16407
  setError(''); setCopied('');
15531
- if (k !== 'tarot') {
16408
+ if (k !== 'tarot' && k !== 'mbti') {
15532
16409
  const entry = {
15533
16410
  kind: k, date, time, gender, address,
15534
16411
  lines: k === 'liuyao' ? lines : '',
@@ -15554,6 +16431,12 @@ function App() {
15554
16431
  const k = kindOf(next);
15555
16432
  setTab(next);
15556
16433
  setError('');
16434
+ if (next === 'mbti') {
16435
+ setNotice('');
16436
+ if (mbAns.filter((v) => v != null).length >= MBTI_MIN) run(next);
16437
+ else setResult(null);
16438
+ return;
16439
+ }
15557
16440
  if (k === 'none') { run(next); return; } // 塔罗不看时间,直接抽牌(与 web-ui 一致)
15558
16441
  const v = manual[k] || (k === 'cast' ? nowParts() : { date: SAMPLE.date, time: SAMPLE.time });
15559
16442
  setDate(v.date);
@@ -15579,6 +16462,15 @@ function App() {
15579
16462
  };
15580
16463
 
15581
16464
  const go = () => {
16465
+ if (tab === 'mbti') {
16466
+ const n = mbAns.filter((v) => v != null).length;
16467
+ if (n < MBTI_MIN) {
16468
+ setNotice(`先答满 ${MBTI_MIN} 题再看结果(现答 ${n} 题,还差 ${MBTI_MIN - n} 题)。`);
16469
+ return;
16470
+ }
16471
+ run(tab);
16472
+ return;
16473
+ }
15582
16474
  if (timeKind !== 'none' && !manual[timeKind]) {
15583
16475
  const t = nowParts();
15584
16476
  setDate(t.date);
@@ -15651,6 +16543,21 @@ function App() {
15651
16543
  } catch (e) { setFx({ question: askText.trim(), ok: false, answer: '追问失败:' + (e && e.message ? e.message : e), evidence: [], confidence: '弱' }); }
15652
16544
  };
15653
16545
 
16546
+ const askInChat = () => {
16547
+ if (!result || !result.r) return;
16548
+ try {
16549
+ const p = Engine.askPrompt ? Engine.askPrompt(result.r, { question: askText.trim() }) : null;
16550
+ const text = p && p.text ? p.text : String(result.r.summary || '');
16551
+ if (sendToComposer(text)) { setChatNote('已发到对话窗,往下看回复。'); return; }
16552
+ const diag = '(' + bridgeDiagText() + ')';
16553
+ if (navigator.clipboard && navigator.clipboard.writeText) {
16554
+ navigator.clipboard.writeText(text).then(
16555
+ () => setChatNote('对话输入框还没就绪' + diag + ',已复制提问;粘进对话窗回车即可。'),
16556
+ () => setChatNote('对话输入框还没就绪' + diag + ',复制也没成功;先点「复制文字结果」再贴。'));
16557
+ } else { setChatNote('对话输入框还没就绪' + diag + ';先点「复制文字结果」再贴进对话窗。'); }
16558
+ } catch (e) { setChatNote('发到对话窗失败:' + (e && e.message ? e.message : e)); }
16559
+ };
16560
+
15654
16561
  const downloadReport = () => {
15655
16562
  if (!result || !result.r) return;
15656
16563
  try {
@@ -15874,10 +16781,27 @@ function App() {
15874
16781
  className: 'mp-input', type: 'number', min: 1, max: 99, value: num3,
15875
16782
  onChange: (e) => setNum3(e.target.value),
15876
16783
  })) : null,
15877
- tab === 'mbti' ? field('24 题分数(1–5,空格或逗号分隔;留空先发问卷)', h('textarea', {
15878
- className: 'mp-textarea', rows: 2, value: answers, placeholder: '如 5 4 3 2 2 5 4 3 5 4 3 2 4 5 3 2 1 2 4 3 5 4 4 2',
15879
- onChange: (e) => setAnswers(e.target.value),
15880
- })) : null,
16784
+ tab === 'mbti' ? h('div', { className: 'mp-quiz-wrap' },
16785
+ h('div', { className: 'mp-lbl' }, '十六型人格问卷 · 4 题一页,按 1(完全不像)到 5(很像我)打分'),
16786
+ h(MbtiQuiz, {
16787
+ answers: mbAns,
16788
+ page: mbPage,
16789
+ onPick: (i, n) => setMbAns((prev) => {
16790
+ const next = prev.slice();
16791
+ next[i] = n;
16792
+ return next;
16793
+ }),
16794
+ onPage: (d) => setMbPage((p) => Math.max(0, Math.min(
16795
+ p + d,
16796
+ Math.max(0, Math.ceil(mbtiInfo().total / MBTI_PER_PAGE) - 1),
16797
+ ))),
16798
+ onSubmit: go,
16799
+ onClear: () => {
16800
+ setMbAns(new Array(mbtiInfo().total).fill(null));
16801
+ setMbPage(0);
16802
+ setResult(null);
16803
+ },
16804
+ })) : null,
15881
16805
  tab === 'trend' ? field('算到几岁', h('input', {
15882
16806
  className: 'mp-input', type: 'number', min: 10, max: 100, value: toAge,
15883
16807
  onChange: (e) => setToAge(e.target.value),
@@ -15926,7 +16850,7 @@ function App() {
15926
16850
  onChange: (e) => { setQuestion(e.target.value); setAskInvalid(false); },
15927
16851
  onBlur: (e) => refreshInterpret(undefined, e.target.value),
15928
16852
  })) : null,
15929
- h('div', { key: 'actions', className: 'mp-actions' },
16853
+ tab === 'mbti' ? null : h('div', { key: 'actions', className: 'mp-actions' },
15930
16854
  h('button', {
15931
16855
  className: 'mp-btn ghost', style: { fontSize: 12, padding: '5px 12px', marginRight: 8 },
15932
16856
  onClick: openSample, title: '用固定示例盘看完整报告(不改动你已填的数据)',
@@ -15958,9 +16882,13 @@ function App() {
15958
16882
  notice ? h('div', { key: 'ntc', className: 'mp-note', role: 'status' }, notice) : null,
15959
16883
 
15960
16884
  (!result && !error) ? h('div', { key: 'empty', className: 'mp-empty' },
15961
- h('b', null, tab === 'ziwei' ? '填好出生信息,点「排盘」' : tab === 'bazi' ? '填好出生信息,点「排盘」'
15962
- : tab === 'tarot' ? '写下想问的事,点「抽牌」' : '写下所问之事,点「起卦」'),
15963
- tab === 'ziwei' || tab === 'bazi' ? '出生日期、时刻与性别即可;出生地只影响真太阳时。' : '起卦时刻已自动取当下,也可以手动改。') : null,
16885
+ h('b', null, tab === 'ziwei' || tab === 'bazi' ? '填好出生信息,点「排盘」'
16886
+ : tab === 'tarot' ? '写下想问的事,点「抽牌」'
16887
+ : tab === 'mbti' ? '按 1–5 给题目打分,答满 12 题就能看结果'
16888
+ : '写下所问之事,点「起卦」'),
16889
+ tab === 'ziwei' || tab === 'bazi' ? '出生日期、时刻与性别即可;出生地只影响真太阳时。'
16890
+ : tab === 'mbti' ? '4 题一页,答案只在本地参与计算,不会上传。'
16891
+ : '起卦时刻已自动取当下,也可以手动改。') : null,
15964
16892
 
15965
16893
  result ? h('div', {
15966
16894
  key: 'card',
@@ -16003,14 +16931,20 @@ function App() {
16003
16931
  onClick: downloadCard, title: '生成 PNG 摘要卡(一页读懂 + 首张表),适合直接分享',
16004
16932
  }, '存为摘要长图')) : null,
16005
16933
 
16006
- result ? h('div', { key: 'ask', className: 'mp-askbox' },
16934
+ result ? h('div', { key: 'ask', className: 'mp-askbox', style: { flexWrap: 'wrap' } },
16007
16935
  h('input', {
16008
16936
  className: 'mp-input', value: askText, maxLength: 120,
16009
16937
  placeholder: '追问:如 2027 年事业怎么样(回车提交)',
16010
16938
  onChange: (e) => setAskText(e.target.value),
16011
16939
  onKeyDown: (e) => { if (e.key === 'Enter') doAsk(); },
16012
16940
  }),
16013
- h('button', { className: 'mp-btn ghost', style: { fontSize: 12, padding: '5px 12px' }, onClick: doAsk }, '追问')) : null,
16941
+ h('button', { className: 'mp-btn ghost', style: { fontSize: 12, padding: '5px 12px' }, onClick: doAsk }, '追问'),
16942
+ h('button', {
16943
+ className: 'mp-btn ghost', style: { fontSize: 12, padding: '5px 12px', marginLeft: 8 },
16944
+ onClick: askInChat, title: '把这张盘的摘要和你的问题一起发进 DSH 对话窗,由对话里的模型接着答',
16945
+ }, '问进对话'),
16946
+ (COMPOSER.ref ? null : h('div', { key: 'bridgediag', className: 'mp-note', style: { marginTop: 6, fontSize: 11, flexBasis: '100%' } }, bridgeDiagText())),
16947
+ chatNote ? h('div', { key: 'chatnote', className: 'mp-note', style: { marginTop: 6, fontSize: 12, flexBasis: '100%' } }, chatNote) : null) : null,
16014
16948
 
16015
16949
  fx ? h('div', { key: 'fx', className: 'mp-fx', dangerouslySetInnerHTML: { __html: PP.followupHTML(fx) } }) : null,
16016
16950
 
@@ -16094,6 +17028,25 @@ function safeApply(ctx) {
16094
17028
  return;
16095
17029
  }
16096
17030
 
17031
+ try {
17032
+ COMPOSER.injectType = typeof slots.inject;
17033
+ const sp = typeof slots.spec === 'function' ? slots.spec('conversation.input.right') : null;
17034
+ COMPOSER.spec = sp ? sp.kind + '/' + sp.scope : '未声明';
17035
+ } catch (e) {
17036
+ COMPOSER.spec = '取声明失败';
17037
+ noteRegError(e);
17038
+ }
17039
+ const tryReg = (options, component) => {
17040
+ try {
17041
+ const d = slots.register(options, component);
17042
+ COMPOSER.regs += 1;
17043
+ return d;
17044
+ } catch (e) {
17045
+ noteRegError(e);
17046
+ return null;
17047
+ }
17048
+ };
17049
+
16097
17050
  const waitFor = (key, install) => {
16098
17051
  try {
16099
17052
  if (typeof slots.inject === 'function') {
@@ -16107,12 +17060,59 @@ function safeApply(ctx) {
16107
17060
  }
16108
17061
  };
16109
17062
 
16110
- waitFor('sidebar.panellist', () => slots.register(
16111
- { name: 'sidebar.panellist', id: 'metaphysics', order: 20, label: () => '命理' },
17063
+ waitFor('sidebar.panellist', () => tryReg(
17064
+ { name: 'sidebar.panellist', id: 'metaphysics', order: 20, label: () => '命理 v' + (window.__MP_VER || '') },
16112
17065
  PanelIcon,
16113
17066
  ));
16114
17067
 
16115
- waitFor('main', () => slots.register({ name: 'main', key: 'metaphysics' }, App));
17068
+ waitFor('main', () => tryReg({ name: 'main', key: 'metaphysics' }, (p) => h(PanelGuard, null, h(App, p))));
17069
+
17070
+ const sessionSeat = (key, install) => {
17071
+ const run = () => {
17072
+ try { slots.inject(key, install); }
17073
+ catch (e) {
17074
+ console.error('[metaphysics] 槽位注册失败:' + key, e);
17075
+ try { install(); } catch (e2) { console.error('[metaphysics] 直接注册也失败:' + key, e2); }
17076
+ }
17077
+ };
17078
+ try {
17079
+ if (typeof ctx.effect === 'function') ctx.effect(run, 'metaphysics: ' + key);
17080
+ else run();
17081
+ } catch (e) {
17082
+ console.error('[metaphysics] effect 包裹失败:' + key, e);
17083
+ run();
17084
+ }
17085
+ };
17086
+
17087
+ sessionSeat('conversation.input.right', () => tryReg(
17088
+ { name: 'conversation.input.right', id: 'metaphysics-ask', order: 40 },
17089
+ ComposerBridge,
17090
+ ));
17091
+
17092
+ sessionSeat('conversation.composer.dock', () => tryReg(
17093
+ { name: 'conversation.composer.dock', id: 'metaphysics-dock', order: 60 },
17094
+ ComposerProbe,
17095
+ ));
17096
+
17097
+ let seatTries = 0;
17098
+ const trySeatNow = () => {
17099
+ if (COMPOSER.ref) return;
17100
+ seatTries += 1;
17101
+ const tag = '-' + seatTries;
17102
+ tryReg({ name: 'conversation.input.right', id: 'metaphysics-ask' + tag, order: 40 }, ComposerBridge);
17103
+ tryReg({ name: 'conversation.composer.dock', id: 'metaphysics-dock' + tag, order: 60 }, ComposerProbe);
17104
+ };
17105
+ let seatTriesLeft = 20;
17106
+ const seatRetry = () => {
17107
+ if (COMPOSER.ref || seatTriesLeft <= 0) return;
17108
+ seatTriesLeft -= 1;
17109
+ trySeatNow();
17110
+ setTimeout(seatRetry, 700);
17111
+ };
17112
+ try { setTimeout(seatRetry, 700); } catch (e) { /* 无计时器环境 */ }
17113
+ try {
17114
+ document.addEventListener('pointerdown', () => trySeatNow(), { once: true, capture: true });
17115
+ } catch (e) { /* 非 DOM 环境 */ }
16116
17116
  }
16117
17117
 
16118
17118
  return {