@openephemeris/mcp-server 3.14.0 → 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +8 -6
- package/dist/prompts.js +16 -16
- package/dist/server-sse.js +65 -14
- package/dist/tools/apps/bi-wheel-app.js +12 -3
- package/dist/tools/apps/bodygraph-app.d.ts +9 -5
- package/dist/tools/apps/bodygraph-app.js +207 -9
- package/dist/tools/apps/chart-wheel-app.js +29 -5
- package/dist/tools/apps/location-tools.js +38 -5
- package/dist/tools/apps/moon-phase-app.js +7 -1
- package/dist/tools/dev.js +102 -68
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +15 -1
- package/dist/tools/specialized/bazi.js +6 -6
- package/dist/tools/specialized/bi_wheel.js +3 -2
- package/dist/tools/specialized/chart_wheel.js +3 -2
- package/dist/tools/specialized/comparative.js +2 -2
- package/dist/tools/specialized/hd_bodygraph.js +4 -3
- package/dist/ui/bazi.html +21 -21
- package/dist/ui/bi-wheel.html +335 -293
- package/dist/ui/bodygraph.html +1645 -1615
- package/dist/ui/chart-wheel.html +1966 -1912
- package/dist/ui/moon-phase.html +72 -49
- package/dist/ui/transit-timeline.html +1 -1
- package/dist/ui/vedic-chart.html +21 -21
- package/package.json +5 -1
package/dist/tools/index.d.ts
CHANGED
|
@@ -52,6 +52,16 @@ export declare function initTools(profile?: ToolProfile): Promise<void>;
|
|
|
52
52
|
* Throws an error if any of the required keys are missing or empty.
|
|
53
53
|
*/
|
|
54
54
|
export declare function validateRequired(args: any, requiredKeys: string[]): void;
|
|
55
|
+
/**
|
|
56
|
+
* Returns `value` only if it is one of `allowed`, otherwise `undefined`.
|
|
57
|
+
*
|
|
58
|
+
* Use for any tool argument that gets interpolated into a backend URL/query
|
|
59
|
+
* string. Even though the inputSchema declares an `enum`, the MCP host does
|
|
60
|
+
* not guarantee enum enforcement, so a hostile client could smuggle extra
|
|
61
|
+
* query parameters (e.g. style="dark&admin=1"). Whitelisting here closes that
|
|
62
|
+
* injection vector at the boundary (DATA-6).
|
|
63
|
+
*/
|
|
64
|
+
export declare function pickEnum<T extends string>(value: unknown, allowed: readonly T[]): T | undefined;
|
|
55
65
|
/**
|
|
56
66
|
* Throws an error if only one of the coordinate pair is provided.
|
|
57
67
|
*/
|
package/dist/tools/index.js
CHANGED
|
@@ -44,7 +44,7 @@ export async function initTools(profile) {
|
|
|
44
44
|
const resolvedProfile = (profile || process.env.OPENEPHEMERIS_PROFILE || process.env.ASTROMCP_PROFILE || "dev").toLowerCase();
|
|
45
45
|
// Always register auth tools (available in all profiles).
|
|
46
46
|
await import("./auth.js");
|
|
47
|
-
// Always register the generic proxy tools (
|
|
47
|
+
// Always register the generic proxy tools (dev_read_api + dev_write_api + dev_list_allowed).
|
|
48
48
|
await import("./dev.js");
|
|
49
49
|
if (resolvedProfile === "dev") {
|
|
50
50
|
// ── FULL BUILD: all specialized tools ───────────────────────────
|
|
@@ -93,6 +93,20 @@ export function validateRequired(args, requiredKeys) {
|
|
|
93
93
|
throw new Error(`Missing required arguments: ${missing.join(", ")}`);
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns `value` only if it is one of `allowed`, otherwise `undefined`.
|
|
98
|
+
*
|
|
99
|
+
* Use for any tool argument that gets interpolated into a backend URL/query
|
|
100
|
+
* string. Even though the inputSchema declares an `enum`, the MCP host does
|
|
101
|
+
* not guarantee enum enforcement, so a hostile client could smuggle extra
|
|
102
|
+
* query parameters (e.g. style="dark&admin=1"). Whitelisting here closes that
|
|
103
|
+
* injection vector at the boundary (DATA-6).
|
|
104
|
+
*/
|
|
105
|
+
export function pickEnum(value, allowed) {
|
|
106
|
+
return typeof value === "string" && allowed.includes(value)
|
|
107
|
+
? value
|
|
108
|
+
: undefined;
|
|
109
|
+
}
|
|
96
110
|
/**
|
|
97
111
|
* Throws an error if only one of the coordinate pair is provided.
|
|
98
112
|
*/
|
|
@@ -145,7 +145,7 @@ registerTool({
|
|
|
145
145
|
additionalProperties: false,
|
|
146
146
|
},
|
|
147
147
|
outputSchema: OUTPUT_SCHEMA_IMAGE_AND_JSON,
|
|
148
|
-
annotations: { title: "BaZi
|
|
148
|
+
annotations: { title: "BaZi Ten Gods", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
149
149
|
handler: async (args) => {
|
|
150
150
|
const body = { ...parseBaziArgs(args) };
|
|
151
151
|
applyVisualConfig(body, args);
|
|
@@ -180,7 +180,7 @@ registerTool({
|
|
|
180
180
|
additionalProperties: false,
|
|
181
181
|
},
|
|
182
182
|
outputSchema: OUTPUT_SCHEMA_IMAGE_AND_JSON,
|
|
183
|
-
annotations: { title: "BaZi
|
|
183
|
+
annotations: { title: "BaZi Element Balance", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
184
184
|
handler: async (args) => {
|
|
185
185
|
const body = { ...parseBaziArgs(args) };
|
|
186
186
|
applyVisualConfig(body, args);
|
|
@@ -223,7 +223,7 @@ registerTool({
|
|
|
223
223
|
additionalProperties: false,
|
|
224
224
|
},
|
|
225
225
|
outputSchema: OUTPUT_SCHEMA_IMAGE_AND_JSON,
|
|
226
|
-
annotations: { title: "BaZi
|
|
226
|
+
annotations: { title: "BaZi Luck Pillars", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
227
227
|
handler: async (args) => {
|
|
228
228
|
if (!args.gender) {
|
|
229
229
|
throw new Error("gender is required for luck pillar calculation. " +
|
|
@@ -266,7 +266,7 @@ registerTool({
|
|
|
266
266
|
additionalProperties: false,
|
|
267
267
|
},
|
|
268
268
|
outputSchema: OUTPUT_SCHEMA_IMAGE_AND_JSON,
|
|
269
|
-
annotations: { title: "BaZi
|
|
269
|
+
annotations: { title: "BaZi Annual Pillar", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
270
270
|
handler: async (args) => {
|
|
271
271
|
if (!args.year) {
|
|
272
272
|
throw new Error("year is required. Provide a Gregorian year (1–9999), e.g. year=2025.");
|
|
@@ -321,7 +321,7 @@ registerTool({
|
|
|
321
321
|
additionalProperties: false,
|
|
322
322
|
},
|
|
323
323
|
outputSchema: OUTPUT_SCHEMA_IMAGE_AND_JSON,
|
|
324
|
-
annotations: { title: "BaZi
|
|
324
|
+
annotations: { title: "BaZi Compatibility", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
325
325
|
handler: async (args) => {
|
|
326
326
|
const chartAComponents = parseBaziArgs({
|
|
327
327
|
year: args.chart_a_year,
|
|
@@ -381,7 +381,7 @@ registerTool({
|
|
|
381
381
|
additionalProperties: false,
|
|
382
382
|
},
|
|
383
383
|
outputSchema: OUTPUT_SCHEMA_IMAGE_AND_JSON,
|
|
384
|
-
annotations: { title: "BaZi
|
|
384
|
+
annotations: { title: "BaZi Chart (Visual)", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
385
385
|
handler: async (args) => {
|
|
386
386
|
const { year, month, day, hour } = parseBaziArgs(args);
|
|
387
387
|
const body = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { registerTool, validateRequired } from "../index.js";
|
|
1
|
+
import { registerTool, validateRequired, pickEnum } from "../index.js";
|
|
2
2
|
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
import { OUTPUT_SCHEMA_IMAGE } from "../output-schemas.js";
|
|
4
4
|
const DATETIME_DESC = "ISO 8601 datetime string, e.g. '1990-04-15T14:30:00' (local time at birth location). " +
|
|
@@ -71,7 +71,8 @@ registerTool({
|
|
|
71
71
|
const mappedCode = HOUSE_SYSTEM_MAP[args.house_system] || "P";
|
|
72
72
|
body.configuration = { house_system: mappedCode };
|
|
73
73
|
}
|
|
74
|
-
const
|
|
74
|
+
const style = pickEnum(args.style, ["light", "dark", "mono"]);
|
|
75
|
+
const styleParam = style ? `&style=${style}` : "";
|
|
75
76
|
return await getActiveClient().post(`/visualization/bi-wheel?format=svg&size=800${styleParam}`, body);
|
|
76
77
|
},
|
|
77
78
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { registerTool, validateRequired } from "../index.js";
|
|
1
|
+
import { registerTool, validateRequired, pickEnum } from "../index.js";
|
|
2
2
|
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
import { OUTPUT_SCHEMA_IMAGE } from "../output-schemas.js";
|
|
4
4
|
const DATETIME_DESC = "ISO 8601 datetime string, e.g. '1990-04-15T14:30:00' (local time at birth location). " +
|
|
@@ -68,7 +68,8 @@ registerTool({
|
|
|
68
68
|
const mappedCode = HOUSE_SYSTEM_MAP[args.house_system] || "P";
|
|
69
69
|
body.configuration = { house_system: mappedCode };
|
|
70
70
|
}
|
|
71
|
-
const
|
|
71
|
+
const style = pickEnum(args.style, ["light", "dark", "mono"]);
|
|
72
|
+
const styleParam = style ? `&style=${style}` : "";
|
|
72
73
|
return await getActiveClient().post(`/visualization/chart-wheel?format=svg&size=800${styleParam}`, body);
|
|
73
74
|
},
|
|
74
75
|
});
|
|
@@ -136,7 +136,7 @@ registerTool({
|
|
|
136
136
|
additionalProperties: false,
|
|
137
137
|
},
|
|
138
138
|
outputSchema: OUTPUT_SCHEMA_JSON,
|
|
139
|
-
annotations: { title: "
|
|
139
|
+
annotations: { title: "Synastry House Overlay", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
140
140
|
handler: async (args) => {
|
|
141
141
|
validateRequired(args, [
|
|
142
142
|
"person_a_datetime", "person_a_latitude", "person_a_longitude",
|
|
@@ -179,7 +179,7 @@ registerTool({
|
|
|
179
179
|
additionalProperties: false,
|
|
180
180
|
},
|
|
181
181
|
outputSchema: OUTPUT_SCHEMA_JSON,
|
|
182
|
-
annotations: { title: "
|
|
182
|
+
annotations: { title: "Transits to Natal Chart", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
183
183
|
handler: async (args) => {
|
|
184
184
|
validateRequired(args, ["natal_datetime", "natal_latitude", "natal_longitude"]);
|
|
185
185
|
// Backend expects: { subject: {...}, transit_datetime?: {...} }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { registerTool, validateRequired } from "../index.js";
|
|
1
|
+
import { registerTool, validateRequired, pickEnum } from "../index.js";
|
|
2
2
|
import { getActiveClient } from "../../backend/client.js";
|
|
3
3
|
import { OUTPUT_SCHEMA_JSON } from "../output-schemas.js";
|
|
4
4
|
/** Append 'Z' if no timezone offset is present in the datetime string. */
|
|
@@ -47,8 +47,9 @@ registerTool({
|
|
|
47
47
|
const body = {
|
|
48
48
|
birth_datetime_utc: ensureTimezone(args.datetime),
|
|
49
49
|
};
|
|
50
|
-
const format = args.format || "svg";
|
|
51
|
-
const
|
|
50
|
+
const format = pickEnum(args.format, ["svg", "png"]) || "svg";
|
|
51
|
+
const style = pickEnum(args.style, ["light", "dark", "mono"]);
|
|
52
|
+
const styleParam = style ? `&style=${style}` : "";
|
|
52
53
|
return await getActiveClient().post(`/visualization/bodygraph?format=${format}&size=800${styleParam}`, body);
|
|
53
54
|
},
|
|
54
55
|
});
|
package/dist/ui/bazi.html
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<meta name="color-scheme" content="dark light" />
|
|
7
|
-
<title>BaZi Four Pillars Explorer</title>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="color-scheme" content="dark light" />
|
|
7
|
+
<title>BaZi Four Pillars Explorer</title>
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
<style>*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root{--bg-primary:#090b0f;--bg-secondary:#12161d;--bg-tertiary:#1c222b;--bg-hover:#272e3b;--border:#282e38;--border-subtle:#1d2229;--text-primary:#e1e5eb;--text-secondary:#9da5b1;--text-muted:#6c727b;--accent:#62b3ff;--accent-warm:#e6ac3d;--radius:8px;--radius-sm:4px;--radius-lg:12px;--wuxing-wood:#31aa40;--wuxing-fire:#f14d4c;--wuxing-earth:#ca8a10;--wuxing-metal:#9fafc8;--wuxing-water:#0099e0;--wuxing-wood-bg:#1b451e;--wuxing-fire-bg:#65201e;--wuxing-earth-bg:#583c10;--wuxing-metal-bg:#3c434d;--wuxing-water-bg:#003f60;--dm-border:#d29922;--dm-bg:#201a10;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}@supports (color:color(display-p3 0 0 0)){:root{--bg-primary:color(display-p3 .036047 .0438664 .059078);--bg-secondary:color(display-p3 .0739679 .0866504 .111186);--bg-tertiary:color(display-p3 .114361 .13221 .166818);--bg-hover:color(display-p3 .15679 .180114 .225406);--border:color(display-p3 .161376 .180145 .216325);--border-subtle:color(display-p3 .118728 .13221 .158123);--text-primary:color(display-p3 .883958 .896627 .920543);--text-secondary:color(display-p3 .622745 .646433 .6914);--text-muted:color(display-p3 .429297 .445909 .477439);--accent:color(display-p3 .446063 .694439 1.01077);--accent-warm:color(display-p3 .866978 .684443 .324483);--wuxing-wood:color(display-p3 .343741 .657851 .307102);--wuxing-fire:color(display-p3 .872583 .352089 .325751);--wuxing-earth:color(display-p3 .754604 .550516 .200367);--wuxing-metal:color(display-p3 .636614 .68404 .77495);--wuxing-water:color(display-p3 .226818 .589506 .875825);--wuxing-wood-bg:color(display-p3 .150499 .267148 .135899);--wuxing-fire-bg:color(display-p3 .36387 .143128 .129405);--wuxing-earth-bg:color(display-p3 .328547 .240912 .0972974);--wuxing-metal-bg:color(display-p3 .240898 .260909 .299258);--wuxing-water-bg:color(display-p3 .0921333 .242221 .364185);--dm-border:color(display-p3 .789908 .60997 .245434);--dm-bg:color(display-p3 .119765 .101379 .0671137)}}@supports (color:lab(0% 0 0)){:root{--bg-primary:lab(3.02674% -.179462 -2.15476);--bg-secondary:lab(7.16596% -.441141 -5.41423);--bg-tertiary:lab(12.9118% -.60384 -7.27841);--bg-hover:lab(18.6882% -.727877 -9.08878);--border:lab(18.7165% -.689313 -7.30941);--border-subtle:lab(12.9391% -.548743 -5.49436);--text-primary:lab(90.6878% -.517368 -3.73452);--text-secondary:lab(67.45% -.932217 -7.41489);--text-muted:lab(47.7476% -.701487 -5.56231);--accent:lab(70.4043% -7.21234 -50.7672);--accent-warm:lab(74.5248% 14.3487 62.5064);--wuxing-wood:lab(61.5358% -50.6384 43.17);--wuxing-fire:lab(57.3944% 63.124 38.0626);--wuxing-earth:lab(62.7263% 19.0613 64.9727);--wuxing-metal:lab(70.8424% -1.5474 -14.6858);--wuxing-water:lab(59.1191% -14.8305 -49.6808);--wuxing-wood-bg:lab(25.5679% -22.4482 18.6532);--wuxing-fire-bg:lab(23.6033% 31.5073 18.7032);--wuxing-earth-bg:lab(28.0231% 9.19801 30.8603);--wuxing-metal-bg:lab(28.0014% -.778109 -7.34471);--wuxing-water-bg:lab(24.4811% -7.8918 -24.9024);--dm-border:lab(67.5362% 14.8294 64.5914);--dm-bg:lab(9.55197% 1.60985 7.31485)}}@media (prefers-color-scheme:light){:root{--bg-primary:#f3f5f9;--bg-secondary:#e7ebf2;--bg-tertiary:#d8dee8;--bg-hover:#d0d8e5;--border:#bdc5d1;--border-subtle:#d2d8e2;--text-primary:#0f1216;--text-secondary:#414853;--text-muted:#6c727b;--accent:#0065b2;--accent-warm:#966800;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--wuxing-wood:#005f0e;--wuxing-fire:#a20519;--wuxing-earth:#805300;--wuxing-metal:#48566c;--wuxing-water:#005380;--wuxing-wood-bg:#c0e3c0;--wuxing-fire-bg:#fdc9c4;--wuxing-earth-bg:#f2daba;--wuxing-metal-bg:#d6dfec;--wuxing-water-bg:#b4defc;--dm-border:#966800;--dm-bg:#f6ede0}@supports (color:color(display-p3 0 0 0)){:root{--bg-primary:color(display-p3 .95471 .961142 .973263);--bg-secondary:color(display-p3 .909797 .922535 .946581);--bg-tertiary:color(display-p3 .852455 .871315 .906983);--bg-hover:color(display-p3 .821139 .84609 .893374);--border:color(display-p3 .745559 .770059 .816515);--border-subtle:color(display-p3 .826933 .845684 .881152);--text-primary:color(display-p3 .0608605 .0691527 .0850621);--text-secondary:color(display-p3 .261513 .281801 .32064);--text-muted:color(display-p3 .429297 .445909 .477439);--accent:color(display-p3 .143802 .388951 .683672);--accent-warm:color(display-p3 .596632 .396992 -.158844);--wuxing-wood:color(display-p3 .0968244 .374385 .0683277);--wuxing-fire:color(display-p3 .580784 .119608 .128804);--wuxing-earth:color(display-p3 .489279 .326588 -.0287041);--wuxing-metal:color(display-p3 .294019 .335238 .415486);--wuxing-water:color(display-p3 -.0775243 .316124 .547443);--wuxing-wood-bg:color(display-p3 .778394 .884493 .76257);--wuxing-fire-bg:color(display-p3 .959986 .795906 .773571);--wuxing-earth-bg:color(display-p3 .93331 .858308 .743829);--wuxing-metal-bg:color(display-p3 .84663 .871726 .919277);--wuxing-water-bg:color(display-p3 .739548 .864306 .975015);--dm-border:color(display-p3 .596632 .396992 -.158844);--dm-bg:color(display-p3 .957692 .931855 .88349)}}@supports (color:lab(0% 0 0)){:root{--bg-primary:lab(96.5044% -.267148 -1.87216);--bg-secondary:lab(93.0079% -.518024 -3.73493);--bg-tertiary:lab(88.3503% -.749856 -5.58736);--bg-hover:lab(86.0117% -.963002 -7.43048);--border:lab(79.0511% -.952899 -7.42528);--border-subtle:lab(86.0302% -.748128 -5.58645);--text-primary:lab(5.23796% -.285208 -3.0582);--text-secondary:lab(30.3224% -.794604 -7.35167);--text-muted:lab(47.7476% -.701487 -5.56231);--accent:lab(41.1205% -1.70796 -50.1423);--accent-warm:lab(47.6194% 20.1868 104.51);--wuxing-wood:lab(34.5429% -45.3762 42.1829);--wuxing-fire:lab(34.3495% 57.1812 37.8813);--wuxing-earth:lab(39.4811% 17.4358 64.8795);--wuxing-metal:lab(36.0172% -1.09252 -14.5181);--wuxing-water:lab(32.2784% -8.65509 -43.065);--wuxing-wood-bg:lab(86.8562% -16.7675 13.1583);--wuxing-fire-bg:lab(85.5193% 18.485 10.0423);--wuxing-earth-bg:lab(88.4503% 4.9783 19.0112);--wuxing-metal-bg:lab(88.3319% -.966102 -7.43206);--wuxing-water-bg:lab(86.1283% -8.87641 -19.3614);--dm-border:lab(47.6194% 20.1868 104.51);--dm-bg:lab(94.2538% 1.22291 7.5882)}}}html,body{background:var(--bg-primary);width:100%;height:100%;color:var(--text-primary);font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px;line-height:1.5;overflow:hidden}body{flex-direction:column;display:flex}#app{flex-direction:column;flex:1;display:flex;overflow:hidden}#loading-state{color:var(--text-secondary);flex-direction:column;flex:1;justify-content:center;align-items:center;gap:12px;display:flex}#loading-state[hidden]{display:none}.oe-loader{width:48px;height:48px;animation:1.2s linear infinite spin}.oe-loader circle{fill:none;stroke:var(--accent);stroke-width:3px;stroke-dasharray:200;stroke-dashoffset:80px;stroke-linecap:round}@keyframes spin{to{transform:rotate(360deg)}}.chart-status{background:var(--bg-secondary);border-top:1px solid var(--border-subtle);color:var(--text-muted);opacity:0;z-index:50;padding:6px 14px;font-size:11px;transition:opacity .2s,transform .2s;position:fixed;bottom:0;left:0;right:0;transform:translateY(100%)}.chart-status.visible{opacity:1;transform:translateY(0)}#bazi-container{flex-direction:column;flex:1;gap:16px;padding:16px;display:flex;overflow-y:auto}#bazi-container[hidden]{display:none}.bazi-pillars{grid-template-columns:repeat(4,1fr);gap:8px;animation:.6s ease-out bazi-fade-in;display:grid}@keyframes bazi-fade-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.bazi-pillar{border:1px solid var(--border-subtle);border-radius:var(--radius);background:var(--bg-secondary);cursor:pointer;flex-direction:column;transition:border-color .15s,box-shadow .15s,transform .15s;display:flex;overflow:hidden}.bazi-pillar:hover{border-color:var(--border);transform:translateY(-1px);box-shadow:0 2px 12px rgba(0,0,0,.1);box-shadow:0 2px 12px color(display-p3 0 0 0/.1);box-shadow:0 2px 12px lab(0% 0 0/.1)}.bazi-pillar--dm{border-color:var(--dm-border);background:var(--dm-bg);box-shadow:inset 3px 0 0 var(--dm-border), inset -3px 0 0 var(--dm-border)}.bazi-pillar--dm:hover{border-color:var(--dm-border);box-shadow:inset 3px 0 0 var(--dm-border), inset -3px 0 0 var(--dm-border), 0 4px 16px rgba(210,153,34,.15)}@supports (color:color(display-p3 0 0 0)){.bazi-pillar--dm:hover{box-shadow:inset 3px 0 0 var(--dm-border), inset -3px 0 0 var(--dm-border), 0 4px 16px color(display-p3 .789908 .60997 .245434/.15)}}@supports (color:lab(0% 0 0)){.bazi-pillar--dm:hover{box-shadow:inset 3px 0 0 var(--dm-border), inset -3px 0 0 var(--dm-border), 0 4px 16px lab(67.5362% 14.8294 64.5914/.15)}}.bazi-pillar-header{text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);background:var(--bg-tertiary);border-bottom:1px solid var(--border-subtle);justify-content:center;align-items:center;gap:4px;padding:6px 8px;font-size:10px;font-weight:700;display:flex}.bazi-pillar--dm .bazi-pillar-header{color:var(--dm-border)}.dm-badge{background:var(--dm-border);color:#090b0f;color:color(display-p3 .036047 .0438664 .059078);color:lab(3.02674% -.179462 -2.15476);letter-spacing:.04em;border-radius:10px;padding:1px 5px;font-size:9px;font-weight:700}@media (prefers-color-scheme:light){.dm-badge{color:#fcf8f1;color:color(display-p3 .985424 .972455 .948118);color:lab(97.7087% .579864 3.77247)}}.bazi-stem-zone{border-bottom:1px solid var(--border-subtle);flex-direction:column;align-items:center;gap:2px;padding:10px 8px;display:flex}.bazi-stem-chinese{font-size:28px;font-weight:700;line-height:1.1}.bazi-stem-name{color:var(--text-secondary);font-size:11px;font-weight:600}.bazi-stem-element{color:var(--bg-primary);border-radius:10px;padding:1px 6px;font-size:10px;font-weight:500}@media (prefers-color-scheme:light){.bazi-stem-element{color:#fff6f8;color:color(display-p3 .992746 .964982 .972458);color:lab(97.5544% 3.37881 .0330448)}}.bazi-branch-zone{border-bottom:1px solid var(--border-subtle);flex-direction:column;align-items:center;gap:2px;padding:10px 8px;display:flex}.bazi-branch-animal-emoji{font-size:22px;line-height:1.1}.bazi-branch-name{color:var(--text-secondary);font-size:11px;font-weight:600}.bazi-branch-animal{color:var(--text-muted);font-size:10px}.bazi-hidden-zone{flex-direction:column;align-items:center;gap:3px;min-height:48px;padding:6px 8px;display:flex}.bazi-hidden-label{text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin-bottom:1px;font-size:9px;font-weight:600}.bazi-hidden-stems{flex-wrap:wrap;justify-content:center;gap:6px;display:flex}.bazi-hidden-stem{flex-direction:column;align-items:center;gap:1px;display:flex}.bazi-hidden-char{font-size:16px;font-weight:600;line-height:1.1}.bazi-hidden-strength{opacity:.7;border-radius:50%;width:6px;height:6px}.bazi-hidden-strength--main{opacity:1}.bazi-hidden-strength--secondary{opacity:.65}.bazi-hidden-strength--residual{opacity:.35}.bazi-ten-god-tag{background:var(--bg-hover);color:var(--text-secondary);border-radius:10px;padding:1px 5px;font-size:9px;font-weight:600}.bazi-balance{background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius);flex-direction:column;gap:8px;padding:12px 14px;animation:.8s ease-out .2s both bazi-fade-in;display:flex}.bazi-balance-header{justify-content:space-between;align-items:center;display:flex}.bazi-balance-title{text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);font-size:11px;font-weight:700}.bazi-balance-strength{background:var(--bg-hover);color:var(--text-secondary);border-radius:10px;padding:2px 8px;font-size:11px;font-weight:600}.bazi-balance-strength--strong{color:var(--wuxing-wood)}.bazi-balance-strength--weak{color:var(--wuxing-fire)}.bazi-bar-container{border-radius:var(--radius-sm);border:1px solid var(--border-subtle);height:28px;display:flex;overflow:hidden}.bazi-bar-segment{min-width:24px;color:var(--text-primary);justify-content:center;align-items:center;font-size:10px;font-weight:700;transition:flex .4s ease-out;display:flex;position:relative;overflow:hidden}.bazi-bar-segment-label{opacity:.8;white-space:nowrap;font-size:9px}.bazi-bar-legend{flex-wrap:wrap;justify-content:center;gap:12px;display:flex}.bazi-legend-item{color:var(--text-secondary);cursor:pointer;align-items:center;gap:4px;font-size:11px;transition:color .15s;display:flex}.bazi-legend-item:hover{color:var(--text-primary)}.bazi-legend-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px}.bazi-legend-pct{font-variant-numeric:tabular-nums;font-weight:700}.bazi-legend-score{color:var(--text-muted);font-size:10px}.bazi-yongshen{border-radius:var(--radius-sm);background:var(--bg-tertiary);border:1px solid var(--border-subtle);color:var(--text-secondary);justify-content:center;align-items:center;gap:6px;padding:6px 12px;font-size:12px;display:flex}.bazi-yongshen-label{color:var(--text-muted);font-weight:500}.bazi-yongshen-value{font-weight:700}.bazi-drawer-overlay{z-index:60;opacity:0;background:rgba(0,0,0,.4);background:color(display-p3 0 0 0/.4);background:lab(0% 0 0/.4);transition:opacity .2s;position:fixed;top:0;bottom:0;left:0;right:0}.bazi-drawer-overlay.visible{opacity:1}.bazi-drawer{background:var(--bg-secondary);border-top:1px solid var(--border);border-radius:var(--radius-lg) var(--radius-lg) 0 0;z-index:61;max-height:60vh;padding:16px;transition:transform .25s ease-out;position:fixed;bottom:0;left:0;right:0;overflow-y:auto;transform:translateY(100%)}.bazi-drawer.visible{transform:translateY(0)}.bazi-drawer-handle{background:var(--border);border-radius:2px;width:36px;height:4px;margin:0 auto 12px}.bazi-drawer-title{align-items:center;gap:8px;margin-bottom:12px;font-size:15px;font-weight:700;display:flex}.bazi-drawer-section{margin-bottom:12px}.bazi-drawer-section-title{text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin-bottom:6px;font-size:10px;font-weight:700}.bazi-drawer-ten-god{background:var(--bg-tertiary);border-radius:var(--radius-sm);align-items:center;gap:10px;margin-bottom:6px;padding:8px 10px;display:flex}.bazi-drawer-tg-chinese{font-size:22px;font-weight:700}.bazi-drawer-tg-info{flex-direction:column;gap:1px;display:flex}.bazi-drawer-tg-name{font-size:13px;font-weight:600}.bazi-drawer-tg-category{color:var(--text-muted);text-transform:capitalize;font-size:11px}.bazi-drawer-hidden-table{border-collapse:collapse;width:100%;font-size:12px}.bazi-drawer-hidden-table th{text-transform:uppercase;letter-spacing:.04em;color:var(--text-muted);text-align:left;border-bottom:1px solid var(--border-subtle);padding:4px 8px;font-size:10px;font-weight:700}.bazi-drawer-hidden-table td{border-bottom:1px solid var(--border-subtle);color:var(--text-secondary);padding:5px 8px}.bazi-drawer-hidden-table tr:last-child td{border-bottom:none}.bazi-actions{flex-wrap:wrap;gap:8px;animation:.8s ease-out .4s both bazi-fade-in;display:flex}.action-btn{border-radius:var(--radius);border:1px solid var(--border);background:var(--bg-secondary);color:var(--text-secondary);cursor:pointer;align-items:center;gap:4px;padding:8px 14px;font-size:12px;font-weight:600;transition:background .15s,color .15s,box-shadow .15s;display:inline-flex}.action-btn:hover{background:var(--bg-hover);color:var(--text-primary)}.action-btn--primary{color:#cbdfff;color:color(display-p3 .811259 .873257 .992208);color:lab(88.2011% -1.89927 -18.3428);background:linear-gradient(135deg,#1d4587,#32326d);background:linear-gradient(135deg,color(display-p3 .153533 .2659 .511983),color(display-p3 .195291 .194916 .412031));background:linear-gradient(135deg,lab(29.3399% 5.63866 -42.0206),lab(23.3003% 14.2058 -35.0966));border-color:#33548c;border-color:color(display-p3 .229049 .327581 .533696);border-color:lab(35.4848% 1.84765 -35.3079)}.action-btn--primary:hover{background:linear-gradient(135deg,#2151a1,#3d3c86);background:linear-gradient(135deg,color(display-p3 .180134 .31482 .611657),color(display-p3 .239239 .236673 .50744));background:linear-gradient(135deg,lab(34.9279% 7.15981 -48.9835),lab(28.8006% 17.5515 -42.0715));box-shadow:0 2px 12px rgba(29,69,135,.3);box-shadow:0 2px 12px color(display-p3 .153533 .2659 .511983/.3);box-shadow:0 2px 12px lab(29.3399% 5.63866 -42.0206/.3)}.error-state,.empty-state{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:32px;display:flex}.error-icon,.empty-icon{font-size:32px}.tooltip{z-index:100;border-radius:var(--radius-sm);background:var(--bg-tertiary);border:1px solid var(--border);color:var(--text-primary);pointer-events:none;opacity:0;max-width:200px;padding:6px 10px;font-size:11px;transition:opacity .15s;position:fixed}.tooltip.visible{opacity:1}@media (max-width:360px){.bazi-stem-chinese{font-size:22px}.bazi-branch-animal-emoji{font-size:18px}.bazi-hidden-char{font-size:13px}.bazi-pillar-header{padding:4px 6px;font-size:9px}#bazi-container{gap:10px;padding:10px}.bazi-pillars{gap:4px}}.chart-layer{opacity:1;transform-origin:310px 310px;animation:.5s cubic-bezier(.16,1,.3,1) both svgLayerAssemble;animation-delay:calc(var(--layer-index,0) * 80ms);transform:scale(1)}@keyframes svgLayerAssemble{0%{opacity:0;transform:scale(.97)}to{opacity:1;transform:scale(1)}}.floating-action-btn:after{content:"";background:linear-gradient(90deg,transparent,rgba(255,255,255,.2),transparent);width:50%;height:100%;animation:3s 2s infinite shimmer;position:absolute;top:0;left:-100%;transform:skew(-20deg)}@keyframes shimmer{0%{left:-100%}20%{left:200%}to{left:200%}}@keyframes slideUpFadeIn{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUpFadeInPanel{0%{opacity:0;transform:translateY(12px)scale(.98)}to{opacity:1;transform:translateY(0)scale(1)}}
|
|
11
11
|
/*$vite$:1*/</style>
|
|
12
|
-
</head>
|
|
13
|
-
<body>
|
|
14
|
-
<div id="app">
|
|
15
|
-
<div id="loading-state">
|
|
16
|
-
<svg class="oe-loader" viewBox="0 0 100 100"><circle cx="50" cy="50" r="45"/></svg>
|
|
17
|
-
<p>Computing Four Pillars…</p>
|
|
18
|
-
</div>
|
|
19
|
-
<div id="chart-status" class="chart-status" role="status" aria-live="polite"></div>
|
|
20
|
-
<div id="bazi-container" hidden></div>
|
|
21
|
-
<div id="tooltip" class="tooltip"
|
|
22
|
-
</div>
|
|
23
|
-
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="app">
|
|
15
|
+
<div id="loading-state">
|
|
16
|
+
<svg class="oe-loader" viewBox="0 0 100 100"><circle cx="50" cy="50" r="45"/></svg>
|
|
17
|
+
<p>Computing Four Pillars…</p>
|
|
18
|
+
</div>
|
|
19
|
+
<div id="chart-status" class="chart-status" role="status" aria-live="polite"></div>
|
|
20
|
+
<div id="bazi-container" hidden></div>
|
|
21
|
+
<div id="tooltip" class="tooltip" aria-live="polite"></div>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
24
|
<script type="module">(function() {
|
|
25
25
|
let e2 = document.createElement("link").relList;
|
|
26
26
|
if (e2 && e2.supports && e2.supports("modulepreload")) return;
|
|
@@ -209,5 +209,5 @@ function A(e2) {
|
|
|
209
209
|
}
|
|
210
210
|
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", S) : S();
|
|
211
211
|
</script>
|
|
212
|
-
</body>
|
|
213
|
-
</html>
|
|
212
|
+
</body>
|
|
213
|
+
</html>
|