@roxyapi/ui 0.3.0 → 0.4.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/AGENTS.md +228 -29
- package/README.md +291 -19
- package/dist/cdn/components/ashtakavarga-grid.js +74 -19
- package/dist/cdn/components/ashtakavarga-grid.js.map +2 -2
- package/dist/cdn/components/biorhythm-chart.js +18 -4
- package/dist/cdn/components/biorhythm-chart.js.map +2 -2
- package/dist/cdn/components/choghadiya-grid.js +47 -12
- package/dist/cdn/components/choghadiya-grid.js.map +3 -3
- package/dist/cdn/components/compatibility-card.js +21 -7
- package/dist/cdn/components/compatibility-card.js.map +2 -2
- package/dist/cdn/components/dasha-timeline.js +113 -28
- package/dist/cdn/components/dasha-timeline.js.map +3 -3
- package/dist/cdn/components/data.js +27 -13
- package/dist/cdn/components/data.js.map +2 -2
- package/dist/cdn/components/divisional-chart.js +225 -118
- package/dist/cdn/components/divisional-chart.js.map +4 -4
- package/dist/cdn/components/dosha-card.js +18 -4
- package/dist/cdn/components/dosha-card.js.map +2 -2
- package/dist/cdn/components/endpoint-form.js +25 -11
- package/dist/cdn/components/endpoint-form.js.map +2 -2
- package/dist/cdn/components/guna-milan.js +20 -6
- package/dist/cdn/components/guna-milan.js.map +2 -2
- package/dist/cdn/components/hexagram.js +22 -8
- package/dist/cdn/components/hexagram.js.map +2 -2
- package/dist/cdn/components/horoscope-card.js +20 -6
- package/dist/cdn/components/horoscope-card.js.map +2 -2
- package/dist/cdn/components/kp-chart.js +19 -5
- package/dist/cdn/components/kp-chart.js.map +2 -2
- package/dist/cdn/components/kp-planets-table.js +17 -3
- package/dist/cdn/components/kp-planets-table.js.map +2 -2
- package/dist/cdn/components/kp-ruling-planets.js +17 -3
- package/dist/cdn/components/kp-ruling-planets.js.map +2 -2
- package/dist/cdn/components/location-search.js +18 -4
- package/dist/cdn/components/location-search.js.map +2 -2
- package/dist/cdn/components/moon-phase.js +27 -13
- package/dist/cdn/components/moon-phase.js.map +2 -2
- package/dist/cdn/components/nakshatra-card.js +16 -2
- package/dist/cdn/components/nakshatra-card.js.map +2 -2
- package/dist/cdn/components/natal-chart.js +79 -40
- package/dist/cdn/components/natal-chart.js.map +3 -3
- package/dist/cdn/components/numerology-card.js +18 -4
- package/dist/cdn/components/numerology-card.js.map +2 -2
- package/dist/cdn/components/panchang-table.js +53 -25
- package/dist/cdn/components/panchang-table.js.map +3 -3
- package/dist/cdn/components/shadbala-table.js +24 -10
- package/dist/cdn/components/shadbala-table.js.map +2 -2
- package/dist/cdn/components/synastry-chart.js +96 -48
- package/dist/cdn/components/synastry-chart.js.map +3 -3
- package/dist/cdn/components/tarot-card.js +17 -3
- package/dist/cdn/components/tarot-card.js.map +2 -2
- package/dist/cdn/components/tarot-spread.js +39 -25
- package/dist/cdn/components/tarot-spread.js.map +2 -2
- package/dist/cdn/components/transits-table.js +18 -4
- package/dist/cdn/components/transits-table.js.map +2 -2
- package/dist/cdn/components/vedic-kundli.js +215 -105
- package/dist/cdn/components/vedic-kundli.js.map +4 -4
- package/dist/cdn/components/vedic-planets-table.js +22 -8
- package/dist/cdn/components/vedic-planets-table.js.map +2 -2
- package/dist/cdn/components/western-planets-table.js +18 -4
- package/dist/cdn/components/western-planets-table.js.map +2 -2
- package/dist/cdn/components/yoga-list.js +17 -3
- package/dist/cdn/components/yoga-list.js.map +2 -2
- package/dist/cdn/roxy-ui.js +1082 -816
- package/dist/cdn/roxy-ui.js.map +4 -4
- package/dist/components/ashtakavarga-grid.d.ts +13 -1
- package/dist/components/ashtakavarga-grid.d.ts.map +1 -1
- package/dist/components/ashtakavarga-grid.js +86 -11
- package/dist/components/ashtakavarga-grid.js.map +2 -2
- package/dist/components/biorhythm-chart.js +14 -0
- package/dist/components/biorhythm-chart.js.map +2 -2
- package/dist/components/choghadiya-grid.d.ts +6 -0
- package/dist/components/choghadiya-grid.d.ts.map +1 -1
- package/dist/components/choghadiya-grid.js +50 -2
- package/dist/components/choghadiya-grid.js.map +2 -2
- package/dist/components/compatibility-card.js +14 -0
- package/dist/components/compatibility-card.js.map +2 -2
- package/dist/components/dasha-timeline.d.ts +10 -0
- package/dist/components/dasha-timeline.d.ts.map +1 -1
- package/dist/components/dasha-timeline.js +135 -4
- package/dist/components/dasha-timeline.js.map +2 -2
- package/dist/components/data.js +14 -0
- package/dist/components/data.js.map +2 -2
- package/dist/components/divisional-chart.d.ts +9 -6
- package/dist/components/divisional-chart.d.ts.map +1 -1
- package/dist/components/divisional-chart.js +546 -251
- package/dist/components/divisional-chart.js.map +4 -4
- package/dist/components/dosha-card.js +14 -0
- package/dist/components/dosha-card.js.map +2 -2
- package/dist/components/endpoint-form.js +14 -0
- package/dist/components/endpoint-form.js.map +2 -2
- package/dist/components/guna-milan.js +14 -0
- package/dist/components/guna-milan.js.map +2 -2
- package/dist/components/hexagram.js +14 -0
- package/dist/components/hexagram.js.map +2 -2
- package/dist/components/horoscope-card.js +14 -0
- package/dist/components/horoscope-card.js.map +2 -2
- package/dist/components/kp-chart.js +14 -0
- package/dist/components/kp-chart.js.map +2 -2
- package/dist/components/kp-planets-table.js +14 -0
- package/dist/components/kp-planets-table.js.map +2 -2
- package/dist/components/kp-ruling-planets.js +14 -0
- package/dist/components/kp-ruling-planets.js.map +2 -2
- package/dist/components/location-search.js +14 -0
- package/dist/components/location-search.js.map +2 -2
- package/dist/components/moon-phase.js +14 -0
- package/dist/components/moon-phase.js.map +2 -2
- package/dist/components/nakshatra-card.js +14 -0
- package/dist/components/nakshatra-card.js.map +2 -2
- package/dist/components/natal-chart.d.ts.map +1 -1
- package/dist/components/natal-chart.js +76 -6
- package/dist/components/natal-chart.js.map +2 -2
- package/dist/components/numerology-card.js +14 -0
- package/dist/components/numerology-card.js.map +2 -2
- package/dist/components/panchang-table.d.ts +1 -0
- package/dist/components/panchang-table.d.ts.map +1 -1
- package/dist/components/panchang-table.js +37 -1
- package/dist/components/panchang-table.js.map +2 -2
- package/dist/components/shadbala-table.js +14 -0
- package/dist/components/shadbala-table.js.map +2 -2
- package/dist/components/synastry-chart.d.ts +6 -0
- package/dist/components/synastry-chart.d.ts.map +1 -1
- package/dist/components/synastry-chart.js +106 -7
- package/dist/components/synastry-chart.js.map +2 -2
- package/dist/components/tarot-card.js +14 -0
- package/dist/components/tarot-card.js.map +2 -2
- package/dist/components/tarot-spread.js +14 -0
- package/dist/components/tarot-spread.js.map +2 -2
- package/dist/components/transits-table.js +14 -0
- package/dist/components/transits-table.js.map +2 -2
- package/dist/components/vedic-kundli.d.ts +14 -9
- package/dist/components/vedic-kundli.d.ts.map +1 -1
- package/dist/components/vedic-kundli.js +537 -245
- package/dist/components/vedic-kundli.js.map +4 -4
- package/dist/components/vedic-planets-table.js +14 -0
- package/dist/components/vedic-planets-table.js.map +2 -2
- package/dist/components/western-planets-table.js +14 -0
- package/dist/components/western-planets-table.js.map +2 -2
- package/dist/components/yoga-list.js +14 -0
- package/dist/components/yoga-list.js.map +2 -2
- package/dist/index.cjs +1397 -797
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +1278 -678
- package/dist/index.js.map +4 -4
- package/dist/styles/tokens.css +8 -23
- package/dist/utils/base-styles.d.ts.map +1 -1
- package/dist/utils/kundli-render.d.ts +43 -104
- package/dist/utils/kundli-render.d.ts.map +1 -1
- package/dist/utils/kundli-styles.d.ts +13 -0
- package/dist/utils/kundli-styles.d.ts.map +1 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/ashtakavarga-grid.ts +73 -11
- package/src/components/choghadiya-grid.ts +37 -2
- package/src/components/dasha-timeline.ts +135 -4
- package/src/components/divisional-chart.ts +40 -97
- package/src/components/natal-chart.ts +89 -6
- package/src/components/panchang-table.ts +34 -1
- package/src/components/synastry-chart.ts +84 -8
- package/src/components/vedic-kundli.ts +35 -95
- package/src/styles/tokens.css +8 -23
- package/src/utils/base-styles.ts +14 -0
- package/src/utils/kundli-render.ts +609 -270
- package/src/utils/kundli-styles.ts +124 -0
- package/src/version.ts +1 -1
|
@@ -4,41 +4,28 @@ import { PLANET_GLYPH } from '../tokens/index.js';
|
|
|
4
4
|
import type { DivisionalChartResponse } from '../types/index.js';
|
|
5
5
|
import { baseStyles } from '../utils/base-styles.js';
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
type
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
renderNorthHouseGroup,
|
|
13
|
-
renderSouthFrame,
|
|
14
|
-
renderSouthHouseGroup,
|
|
7
|
+
type ChartStyle,
|
|
8
|
+
type KundliViewModel,
|
|
9
|
+
renderKundliStyleTablist,
|
|
10
|
+
renderKundliSvg,
|
|
11
|
+
toKundliViewModel,
|
|
15
12
|
} from '../utils/kundli-render.js';
|
|
13
|
+
import { kundliStyles } from '../utils/kundli-styles.js';
|
|
16
14
|
|
|
17
15
|
/**
|
|
18
16
|
* Divisional chart renderer (D2-D60). Accepts a DivisionalChartResponse and
|
|
19
|
-
* renders the same
|
|
20
|
-
* division metadata and Vargottama planet pills.
|
|
21
|
-
*
|
|
22
|
-
* from that
|
|
17
|
+
* renders the same South / North / East kundli grid as the birth chart, plus
|
|
18
|
+
* division metadata and Vargottama planet pills. A visible tablist lets the
|
|
19
|
+
* end user switch styles at runtime. The varga response carries a graha-keyed
|
|
20
|
+
* `chart.meta` map (no per-rashi buckets), so houses are bucketed from that
|
|
21
|
+
* map.
|
|
23
22
|
*/
|
|
24
23
|
@customElement('roxy-divisional-chart')
|
|
25
24
|
export class RoxyDivisionalChart extends LitElement {
|
|
26
25
|
static styles = [
|
|
27
26
|
baseStyles,
|
|
27
|
+
kundliStyles,
|
|
28
28
|
css`
|
|
29
|
-
.wrap {
|
|
30
|
-
display: grid;
|
|
31
|
-
gap: var(--roxy-space-md, 1rem);
|
|
32
|
-
}
|
|
33
|
-
.header {
|
|
34
|
-
display: grid;
|
|
35
|
-
gap: var(--roxy-space-xs, 0.25rem);
|
|
36
|
-
}
|
|
37
|
-
.title {
|
|
38
|
-
font-size: var(--roxy-text-lg, 1.125rem);
|
|
39
|
-
font-weight: var(--roxy-weight-bold, 600);
|
|
40
|
-
margin: 0;
|
|
41
|
-
}
|
|
42
29
|
.division-meta {
|
|
43
30
|
font-size: var(--roxy-text-sm, 0.875rem);
|
|
44
31
|
color: var(--roxy-muted, #71717a);
|
|
@@ -51,46 +38,6 @@ export class RoxyDivisionalChart extends LitElement {
|
|
|
51
38
|
padding-left: var(--roxy-space-sm, 0.5rem);
|
|
52
39
|
margin: 0;
|
|
53
40
|
}
|
|
54
|
-
svg {
|
|
55
|
-
display: block;
|
|
56
|
-
width: 100%;
|
|
57
|
-
max-width: 360px;
|
|
58
|
-
margin: 0 auto;
|
|
59
|
-
}
|
|
60
|
-
.line {
|
|
61
|
-
fill: transparent;
|
|
62
|
-
stroke: var(--roxy-border, #e4e4e7);
|
|
63
|
-
}
|
|
64
|
-
.sign-text {
|
|
65
|
-
fill: var(--roxy-muted, #71717a);
|
|
66
|
-
font-size: 9px;
|
|
67
|
-
font-weight: 500;
|
|
68
|
-
font-family: var(--roxy-font-sans);
|
|
69
|
-
}
|
|
70
|
-
.planet-text {
|
|
71
|
-
fill: var(--roxy-fg, #0a0a0a);
|
|
72
|
-
font-size: 11px;
|
|
73
|
-
font-weight: 600;
|
|
74
|
-
font-family: var(--roxy-font-sans);
|
|
75
|
-
}
|
|
76
|
-
.house-num {
|
|
77
|
-
fill: var(--roxy-muted, #71717a);
|
|
78
|
-
font-size: 9px;
|
|
79
|
-
font-weight: 400;
|
|
80
|
-
font-family: var(--roxy-font-sans);
|
|
81
|
-
}
|
|
82
|
-
.lagna-marker {
|
|
83
|
-
fill: var(--roxy-accent-fg, #b45309);
|
|
84
|
-
font-size: 8px;
|
|
85
|
-
font-weight: 700;
|
|
86
|
-
font-family: var(--roxy-font-sans);
|
|
87
|
-
letter-spacing: 0.05em;
|
|
88
|
-
}
|
|
89
|
-
.lagna-bg {
|
|
90
|
-
fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 12%, transparent);
|
|
91
|
-
stroke: color-mix(in srgb, var(--roxy-accent, #f59e0b) 45%, transparent);
|
|
92
|
-
stroke-width: 0.8;
|
|
93
|
-
}
|
|
94
41
|
.vargottama-row {
|
|
95
42
|
display: flex;
|
|
96
43
|
flex-wrap: wrap;
|
|
@@ -122,58 +69,54 @@ export class RoxyDivisionalChart extends LitElement {
|
|
|
122
69
|
data: DivisionalChartResponse | null = null;
|
|
123
70
|
|
|
124
71
|
@property({ type: String, reflect: true, attribute: 'chart-style' })
|
|
125
|
-
chartStyle:
|
|
72
|
+
chartStyle: ChartStyle = 'north';
|
|
73
|
+
|
|
74
|
+
private setStyle = (next: ChartStyle) => {
|
|
75
|
+
this.chartStyle = next;
|
|
76
|
+
};
|
|
126
77
|
|
|
127
|
-
private
|
|
128
|
-
if (!this.data?.chart?.meta) return
|
|
129
|
-
|
|
78
|
+
private viewModel(): KundliViewModel | null {
|
|
79
|
+
if (!this.data?.chart?.meta) return null;
|
|
80
|
+
const { division } = this.data;
|
|
81
|
+
const label = `D${division.number} ${division.name}`;
|
|
82
|
+
return toKundliViewModel(this.data.chart.meta, label);
|
|
130
83
|
}
|
|
131
84
|
|
|
132
85
|
render() {
|
|
133
|
-
|
|
86
|
+
const vm = this.viewModel();
|
|
87
|
+
if (!this.data || !vm)
|
|
134
88
|
return html`<div class="roxy-empty" role="status">No divisional chart data</div>`;
|
|
135
89
|
|
|
136
90
|
const { division, vargottama } = this.data;
|
|
137
|
-
const houses = this.buildHouses();
|
|
138
|
-
const style = this.chartStyle;
|
|
139
|
-
const frame =
|
|
140
|
-
style === 'north'
|
|
141
|
-
? renderNorthFrame()
|
|
142
|
-
: style === 'east'
|
|
143
|
-
? renderEastFrame()
|
|
144
|
-
: renderSouthFrame();
|
|
145
|
-
const houseGroup =
|
|
146
|
-
style === 'north'
|
|
147
|
-
? renderNorthHouseGroup
|
|
148
|
-
: style === 'east'
|
|
149
|
-
? renderEastHouseGroup
|
|
150
|
-
: renderSouthHouseGroup;
|
|
151
91
|
|
|
152
92
|
return html`<div class="wrap">
|
|
153
93
|
<div class="header">
|
|
154
|
-
<
|
|
155
|
-
|
|
94
|
+
<div>
|
|
95
|
+
<h2 class="title">
|
|
96
|
+
D${division.number} ${division.name}
|
|
97
|
+
${
|
|
98
|
+
division.sanskritName && division.sanskritName !== division.name
|
|
99
|
+
? html`<span class="division-meta"> · ${division.sanskritName}</span>`
|
|
100
|
+
: nothing
|
|
101
|
+
}
|
|
102
|
+
</h2>
|
|
156
103
|
${
|
|
157
|
-
division.
|
|
158
|
-
? html`<
|
|
104
|
+
division.significance
|
|
105
|
+
? html`<p class="significance">${division.significance}</p>`
|
|
159
106
|
: nothing
|
|
160
107
|
}
|
|
161
|
-
</
|
|
162
|
-
${
|
|
163
|
-
division.significance
|
|
164
|
-
? html`<p class="significance">${division.significance}</p>`
|
|
165
|
-
: nothing
|
|
166
|
-
}
|
|
108
|
+
</div>
|
|
109
|
+
${renderKundliStyleTablist(this.chartStyle, this.setStyle)}
|
|
167
110
|
</div>
|
|
168
111
|
|
|
169
112
|
<svg
|
|
170
|
-
viewBox="0 0
|
|
113
|
+
viewBox="0 0 400 400"
|
|
114
|
+
preserveAspectRatio="xMidYMid meet"
|
|
171
115
|
role="img"
|
|
172
116
|
aria-label="D${division.number} ${division.name} divisional chart with twelve sign houses"
|
|
173
117
|
>
|
|
174
118
|
<title>D${division.number} ${division.name}</title>
|
|
175
|
-
${
|
|
176
|
-
${houses.map((h) => houseGroup(h))}
|
|
119
|
+
${renderKundliSvg(vm, this.chartStyle)}
|
|
177
120
|
</svg>
|
|
178
121
|
|
|
179
122
|
${
|
|
@@ -64,7 +64,8 @@ export class RoxyNatalChart extends LitElement {
|
|
|
64
64
|
svg {
|
|
65
65
|
display: block;
|
|
66
66
|
width: 100%;
|
|
67
|
-
max-width:
|
|
67
|
+
max-width: 560px;
|
|
68
|
+
aspect-ratio: 1 / 1;
|
|
68
69
|
height: auto;
|
|
69
70
|
margin: 0 auto;
|
|
70
71
|
}
|
|
@@ -93,10 +94,33 @@ export class RoxyNatalChart extends LitElement {
|
|
|
93
94
|
font-family: var(--roxy-font-sans);
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
/* Below 480px the chart container shrinks to ~320px on phones.
|
|
98
|
+
* Bump in-SVG text up proportionally so the 7px degree band
|
|
99
|
+
* does not collapse below ~6px on screen.
|
|
100
|
+
*/
|
|
101
|
+
@container (max-width: 480px) {
|
|
102
|
+
.sign-glyph,
|
|
103
|
+
.planet-glyph {
|
|
104
|
+
font-size: 18px;
|
|
105
|
+
}
|
|
106
|
+
.planet-deg {
|
|
107
|
+
font-size: 10px;
|
|
108
|
+
}
|
|
109
|
+
.house-num {
|
|
110
|
+
font-size: 12px;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
96
114
|
.planet-deg .retro {
|
|
97
115
|
fill: var(--roxy-danger, #dc2626);
|
|
98
116
|
}
|
|
99
117
|
|
|
118
|
+
.planet-leader {
|
|
119
|
+
stroke: var(--roxy-accent, #f59e0b);
|
|
120
|
+
stroke-width: 0.5;
|
|
121
|
+
opacity: 0.55;
|
|
122
|
+
}
|
|
123
|
+
|
|
100
124
|
.house-num {
|
|
101
125
|
fill: var(--roxy-muted, #71717a);
|
|
102
126
|
font-size: 9px;
|
|
@@ -559,6 +583,10 @@ export class RoxyNatalChart extends LitElement {
|
|
|
559
583
|
}
|
|
560
584
|
|
|
561
585
|
private renderAngleMark(longitude: number, label: string) {
|
|
586
|
+
// Tick AND label share the same angle so the label sits right at the
|
|
587
|
+
// tip of the arrow, where a practitioner expects to find it. The label
|
|
588
|
+
// halo at radius ANGLE_LABEL_R is clear of the wheel rim, so there is
|
|
589
|
+
// no overlap with house dividers despite the shared angle.
|
|
562
590
|
const angle = this.toAngle(longitude);
|
|
563
591
|
const tickInner = polarToCartesian(CENTER, CENTER, OUTER_R, angle);
|
|
564
592
|
const tickOuter = polarToCartesian(CENTER, CENTER, ANGLE_TICK_R, angle);
|
|
@@ -662,16 +690,71 @@ export class RoxyNatalChart extends LitElement {
|
|
|
662
690
|
}
|
|
663
691
|
|
|
664
692
|
private renderPlanets(planets: PlanetEntry[]) {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
693
|
+
// Stellium-aware angular fan-out. Conjunctions within 8° are the norm
|
|
694
|
+
// in professional natal charts (Sun-Mercury-Venus cluster, outer-planet
|
|
695
|
+
// stacks). To keep every glyph legible without losing precision, sort
|
|
696
|
+
// by longitude and push later members forward in angle until they
|
|
697
|
+
// clear a minimum separation, then draw a thin leader line from each
|
|
698
|
+
// displaced glyph back to the planet's true position on the outer
|
|
699
|
+
// rim. Conventional approach used by professional Western natal
|
|
700
|
+
// software; preserves both readability and astronomical accuracy.
|
|
701
|
+
const MIN_SEPARATION = 7;
|
|
702
|
+
type Placed = {
|
|
703
|
+
p: PlanetEntry;
|
|
704
|
+
trueLon: number;
|
|
705
|
+
displayLon: number;
|
|
706
|
+
};
|
|
707
|
+
const sorted: Placed[] = planets
|
|
708
|
+
.filter((p) => Number.isFinite(p.longitude))
|
|
709
|
+
.map((p) => ({
|
|
710
|
+
p,
|
|
711
|
+
trueLon: normalizeLongitude(p.longitude),
|
|
712
|
+
displayLon: normalizeLongitude(p.longitude),
|
|
713
|
+
}))
|
|
714
|
+
.sort((a, b) => a.trueLon - b.trueLon);
|
|
715
|
+
// Forward sweep: clamp each to at least prev + MIN_SEPARATION.
|
|
716
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
717
|
+
const prev = sorted[i - 1];
|
|
718
|
+
const cur = sorted[i];
|
|
719
|
+
if (!prev || !cur) continue;
|
|
720
|
+
const wanted = prev.displayLon + MIN_SEPARATION;
|
|
721
|
+
if (cur.displayLon < wanted) cur.displayLon = wanted;
|
|
722
|
+
}
|
|
723
|
+
// If the cluster overshot 360°, slide everything back equally so the
|
|
724
|
+
// stack stays anchored near the original longitudes.
|
|
725
|
+
const last = sorted[sorted.length - 1];
|
|
726
|
+
if (last && last.displayLon > 360) {
|
|
727
|
+
const shift = last.displayLon - 360;
|
|
728
|
+
for (const s of sorted) s.displayLon -= shift;
|
|
729
|
+
}
|
|
730
|
+
return sorted.map(({ p, trueLon, displayLon }) => {
|
|
731
|
+
const trueAngle = this.toAngle(trueLon);
|
|
732
|
+
const displayAngle = this.toAngle(displayLon);
|
|
733
|
+
const glyphPos = polarToCartesian(CENTER, CENTER, PLANET_R, displayAngle);
|
|
734
|
+
const degPos = polarToCartesian(
|
|
735
|
+
CENTER,
|
|
736
|
+
CENTER,
|
|
737
|
+
PLANET_R - 13,
|
|
738
|
+
displayAngle,
|
|
739
|
+
);
|
|
740
|
+
const rimPos = polarToCartesian(CENTER, CENTER, OUTER_R - 4, trueAngle);
|
|
741
|
+
const leaderInner = polarToCartesian(
|
|
742
|
+
CENTER,
|
|
743
|
+
CENTER,
|
|
744
|
+
PLANET_R + 8,
|
|
745
|
+
displayAngle,
|
|
746
|
+
);
|
|
670
747
|
const glyph = PLANET_GLYPH[capitalize(p.name)] ?? p.name.slice(0, 2);
|
|
671
748
|
const sp = longitudeToSignPosition(p.longitude);
|
|
672
749
|
const retro = p.isRetrograde === true;
|
|
673
750
|
const degLabel = `${sp.degree}°${String(sp.minute).padStart(2, '0')}'`;
|
|
751
|
+
const offset = Math.abs(displayLon - trueLon) > 0.5;
|
|
674
752
|
return svg`<g>
|
|
753
|
+
${
|
|
754
|
+
offset
|
|
755
|
+
? svg`<line class="planet-leader" x1=${rimPos.x} y1=${rimPos.y} x2=${leaderInner.x} y2=${leaderInner.y} />`
|
|
756
|
+
: nothing
|
|
757
|
+
}
|
|
675
758
|
<text class="planet-glyph" x=${glyphPos.x} y=${glyphPos.y} text-anchor="middle" dominant-baseline="central"><title>${p.name}${retro ? ' retrograde' : ''} - ${degLabel} ${p.sign ?? ''}</title>${glyph}</text>
|
|
676
759
|
<text class="planet-deg" x=${degPos.x} y=${degPos.y} text-anchor="middle" dominant-baseline="central">${degLabel}${retro ? svg`<tspan class="retro"> ℞</tspan>` : nothing}</text>
|
|
677
760
|
</g>`;
|
|
@@ -116,6 +116,9 @@ export class RoxyPanchangTable extends LitElement {
|
|
|
116
116
|
]
|
|
117
117
|
: [];
|
|
118
118
|
|
|
119
|
+
const transitions =
|
|
120
|
+
detailed && 'transitions' in detailed ? detailed.transitions : undefined;
|
|
121
|
+
|
|
119
122
|
return html`<div class="wrap" aria-label="Panchang">
|
|
120
123
|
<header class="head">
|
|
121
124
|
<h2 class="title">Panchang</h2>
|
|
@@ -163,6 +166,21 @@ export class RoxyPanchangTable extends LitElement {
|
|
|
163
166
|
}
|
|
164
167
|
</tbody>
|
|
165
168
|
</table>
|
|
169
|
+
${
|
|
170
|
+
transitions
|
|
171
|
+
? html`
|
|
172
|
+
<div class="section">Next transitions</div>
|
|
173
|
+
<table>
|
|
174
|
+
<tbody>
|
|
175
|
+
${this.renderTransitionRow('Tithi', transitions.tithi)}
|
|
176
|
+
${this.renderTransitionRow('Nakshatra', transitions.nakshatra)}
|
|
177
|
+
${this.renderTransitionRow('Yoga', transitions.yoga)}
|
|
178
|
+
${this.renderTransitionRow('Karana', transitions.karana)}
|
|
179
|
+
</tbody>
|
|
180
|
+
</table>
|
|
181
|
+
`
|
|
182
|
+
: nothing
|
|
183
|
+
}
|
|
166
184
|
${
|
|
167
185
|
this.detail === 'detailed' &&
|
|
168
186
|
(muhurtas.some((m) => !!m[1]) || inauspicious.some((m) => !!m[1]))
|
|
@@ -199,6 +217,19 @@ export class RoxyPanchangTable extends LitElement {
|
|
|
199
217
|
</div>`;
|
|
200
218
|
}
|
|
201
219
|
|
|
220
|
+
private renderTransitionRow(
|
|
221
|
+
label: string,
|
|
222
|
+
t: { endsAt?: string; next?: string } | undefined,
|
|
223
|
+
) {
|
|
224
|
+
if (!t?.endsAt) return nothing;
|
|
225
|
+
const when = formatTime(t.endsAt);
|
|
226
|
+
const next = t.next ? ` → ${t.next}` : '';
|
|
227
|
+
return html`<tr>
|
|
228
|
+
<th>${label}</th>
|
|
229
|
+
<td>ends ${when}${next}</td>
|
|
230
|
+
</tr>`;
|
|
231
|
+
}
|
|
232
|
+
|
|
202
233
|
private formatPart(v: unknown): string {
|
|
203
234
|
if (!v) return '';
|
|
204
235
|
if (typeof v === 'string') return v;
|
|
@@ -207,11 +238,13 @@ export class RoxyPanchangTable extends LitElement {
|
|
|
207
238
|
name?: string;
|
|
208
239
|
lord?: string;
|
|
209
240
|
phase?: string;
|
|
241
|
+
paksha?: string;
|
|
210
242
|
end?: string;
|
|
211
243
|
};
|
|
212
244
|
const parts = [
|
|
213
245
|
obj.name,
|
|
214
|
-
obj.
|
|
246
|
+
obj.paksha ? `(${obj.paksha} paksha)` : '',
|
|
247
|
+
obj.lord ? `· ${obj.lord}` : '',
|
|
215
248
|
obj.phase,
|
|
216
249
|
].filter(Boolean);
|
|
217
250
|
return parts.join(' ');
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
NatalChartResponse,
|
|
7
7
|
} from '../types/index.js';
|
|
8
8
|
import { baseStyles } from '../utils/base-styles.js';
|
|
9
|
-
import { polarToCartesian } from '../utils/degree.js';
|
|
9
|
+
import { longitudeToSignPosition, polarToCartesian } from '../utils/degree.js';
|
|
10
10
|
import {
|
|
11
11
|
ASPECT_CLASS,
|
|
12
12
|
formatNumber,
|
|
@@ -72,7 +72,9 @@ export class RoxySynastryChart extends LitElement {
|
|
|
72
72
|
svg {
|
|
73
73
|
display: block;
|
|
74
74
|
width: 100%;
|
|
75
|
-
max-width:
|
|
75
|
+
max-width: 560px;
|
|
76
|
+
aspect-ratio: 1 / 1;
|
|
77
|
+
height: auto;
|
|
76
78
|
margin: 0 auto;
|
|
77
79
|
}
|
|
78
80
|
|
|
@@ -94,6 +96,31 @@ export class RoxySynastryChart extends LitElement {
|
|
|
94
96
|
font-weight: 600;
|
|
95
97
|
font-size: 13px;
|
|
96
98
|
}
|
|
99
|
+
.person-tag {
|
|
100
|
+
font-size: 7px;
|
|
101
|
+
font-weight: 700;
|
|
102
|
+
opacity: 0.85;
|
|
103
|
+
}
|
|
104
|
+
.planet-deg {
|
|
105
|
+
fill: var(--roxy-muted, #71717a);
|
|
106
|
+
font-size: 7px;
|
|
107
|
+
font-family: var(--roxy-font-sans);
|
|
108
|
+
}
|
|
109
|
+
.planet-deg .retro {
|
|
110
|
+
fill: var(--roxy-danger, #dc2626);
|
|
111
|
+
}
|
|
112
|
+
.asc-tick {
|
|
113
|
+
stroke: var(--roxy-accent-fg, #b45309);
|
|
114
|
+
stroke-width: 1;
|
|
115
|
+
opacity: 0.75;
|
|
116
|
+
}
|
|
117
|
+
.asc-label {
|
|
118
|
+
fill: var(--roxy-accent-fg, #b45309);
|
|
119
|
+
font-size: 9px;
|
|
120
|
+
font-weight: 700;
|
|
121
|
+
font-family: var(--roxy-font-sans);
|
|
122
|
+
letter-spacing: 0.04em;
|
|
123
|
+
}
|
|
97
124
|
.aspect {
|
|
98
125
|
stroke-width: 0.8;
|
|
99
126
|
fill: none;
|
|
@@ -316,7 +343,8 @@ export class RoxySynastryChart extends LitElement {
|
|
|
316
343
|
/>
|
|
317
344
|
${this.renderSpokes()} ${this.renderSigns()}
|
|
318
345
|
${this.renderInterAspectLines(p1Planets, p2Planets, interAspects)}
|
|
319
|
-
${this.renderRing(p1Planets, P1_R, 'p1')} ${this.renderRing(p2Planets, P2_R, 'p2')}
|
|
346
|
+
${this.renderRing(p1Planets, P1_R, 'p1', 1)} ${this.renderRing(p2Planets, P2_R, 'p2', 2)}
|
|
347
|
+
${this.renderAscendants(this.data)}
|
|
320
348
|
</svg>
|
|
321
349
|
<div class="legend-row">
|
|
322
350
|
<span><span class="swatch" style="background: var(--roxy-accent)"></span>Person 1</span>
|
|
@@ -376,20 +404,68 @@ export class RoxySynastryChart extends LitElement {
|
|
|
376
404
|
});
|
|
377
405
|
}
|
|
378
406
|
|
|
379
|
-
private renderRing(
|
|
407
|
+
private renderRing(
|
|
408
|
+
planets: PlanetEntry[],
|
|
409
|
+
radius: number,
|
|
410
|
+
cls: string,
|
|
411
|
+
personIndex: 1 | 2,
|
|
412
|
+
) {
|
|
380
413
|
return planets.map((p) => {
|
|
381
414
|
if (!Number.isFinite(p.longitude)) return nothing;
|
|
382
|
-
const
|
|
415
|
+
const angle = this.toAngle(p.longitude);
|
|
416
|
+
const pos = polarToCartesian(CENTER, CENTER, radius, angle);
|
|
417
|
+
// Degree label sits one tier inward from the glyph so the two
|
|
418
|
+
// concentric rings never blur their numbers into the aspect lines.
|
|
419
|
+
const degOffset = personIndex === 1 ? -12 : -10;
|
|
420
|
+
const degPos = polarToCartesian(
|
|
383
421
|
CENTER,
|
|
384
422
|
CENTER,
|
|
385
|
-
radius,
|
|
386
|
-
|
|
423
|
+
radius + degOffset,
|
|
424
|
+
angle,
|
|
387
425
|
);
|
|
388
426
|
const glyph = PLANET_GLYPH[capitalize(p.name)] ?? p.name.slice(0, 2);
|
|
389
|
-
|
|
427
|
+
const sp = longitudeToSignPosition(p.longitude);
|
|
428
|
+
const retro = p.isRetrograde === true;
|
|
429
|
+
const degLabel = `${sp.degree}°${String(sp.minute).padStart(2, '0')}'`;
|
|
430
|
+
const tooltip = `${p.name}${retro ? ' retrograde' : ''} - ${degLabel} ${sp.sign}`;
|
|
431
|
+
return svg`<g>
|
|
432
|
+
<text class=${cls} x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central"><title>${tooltip}</title>${glyph}<tspan class="person-tag" dy="-0.55em" dx="0.15em">${personIndex}</tspan></text>
|
|
433
|
+
<text class="planet-deg" x=${degPos.x} y=${degPos.y} text-anchor="middle" dominant-baseline="central">${sp.degree}°${retro ? svg`<tspan class="retro"> ℞</tspan>` : nothing}</text>
|
|
434
|
+
</g>`;
|
|
390
435
|
});
|
|
391
436
|
}
|
|
392
437
|
|
|
438
|
+
/**
|
|
439
|
+
* Ascendant markers for both people. Drawn as small spokes at the inner
|
|
440
|
+
* rim with the label outside, so the two rising signs are immediately
|
|
441
|
+
* scannable on the wheel without depending on tooltips.
|
|
442
|
+
*/
|
|
443
|
+
private renderAscendants(data: SynastryWithPlanets) {
|
|
444
|
+
const items: ReturnType<typeof svg>[] = [];
|
|
445
|
+
const make = (
|
|
446
|
+
asc: { sign: string; degree: number } | undefined,
|
|
447
|
+
personIndex: 1 | 2,
|
|
448
|
+
) => {
|
|
449
|
+
if (!asc) return;
|
|
450
|
+
const signIdx = SIGNS_ORDER.findIndex(
|
|
451
|
+
(s) => s.toLowerCase() === asc.sign.toLowerCase(),
|
|
452
|
+
);
|
|
453
|
+
if (signIdx === -1) return;
|
|
454
|
+
const longitude = signIdx * 30 + asc.degree;
|
|
455
|
+
const angle = this.toAngle(longitude);
|
|
456
|
+
const innerR = personIndex === 1 ? P1_R + 14 : P2_R + 14;
|
|
457
|
+
const tickPos = polarToCartesian(CENTER, CENTER, innerR, angle);
|
|
458
|
+
const labelPos = polarToCartesian(CENTER, CENTER, OUTER_R + 14, angle);
|
|
459
|
+
items.push(svg`<g>
|
|
460
|
+
<line class="asc-tick" x1=${tickPos.x} y1=${tickPos.y} x2=${labelPos.x} y2=${labelPos.y} />
|
|
461
|
+
<text class="asc-label" x=${labelPos.x} y=${labelPos.y} text-anchor="middle" dominant-baseline="central">Asc${personIndex}</text>
|
|
462
|
+
</g>`);
|
|
463
|
+
};
|
|
464
|
+
make(data.person1?.ascendant, 1);
|
|
465
|
+
make(data.person2?.ascendant, 2);
|
|
466
|
+
return items;
|
|
467
|
+
}
|
|
468
|
+
|
|
393
469
|
private renderInterAspectLines(
|
|
394
470
|
p1: PlanetEntry[],
|
|
395
471
|
p2: PlanetEntry[],
|
|
@@ -1,126 +1,66 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { html, LitElement } from 'lit';
|
|
2
2
|
import { customElement, property } from 'lit/decorators.js';
|
|
3
3
|
import type { BirthChartResponse } from '../types/index.js';
|
|
4
4
|
import { baseStyles } from '../utils/base-styles.js';
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
renderNorthHouseGroup,
|
|
12
|
-
renderSouthFrame,
|
|
13
|
-
renderSouthHouseGroup,
|
|
6
|
+
type ChartStyle,
|
|
7
|
+
type KundliViewModel,
|
|
8
|
+
renderKundliStyleTablist,
|
|
9
|
+
renderKundliSvg,
|
|
10
|
+
toKundliViewModel,
|
|
14
11
|
} from '../utils/kundli-render.js';
|
|
12
|
+
import { kundliStyles } from '../utils/kundli-styles.js';
|
|
15
13
|
|
|
16
14
|
/**
|
|
17
15
|
* Vedic kundli (D1 Rashi chart). Pass `data` from /vedic-astrology/birth-chart.
|
|
18
|
-
* Three render styles
|
|
19
|
-
*
|
|
20
|
-
* style is purely a layout choice.
|
|
21
|
-
* whole-degree, with an SVG tooltip carrying exact position, nakshatra, pada,
|
|
22
|
-
* and avastha.
|
|
16
|
+
* Three regional render styles are available; the visible tablist lets the
|
|
17
|
+
* end user switch between South / North / East at any time. The same planet-
|
|
18
|
+
* in-sign data feeds every style, so the toggle is purely a layout choice.
|
|
23
19
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
20
|
+
* Each planet shows its abbreviation and whole-degree-within-sign, with an
|
|
21
|
+
* SVG tooltip carrying exact position, nakshatra, pada, and avastha. The host
|
|
22
|
+
* page sets the initial style via `chart-style` attribute; from there the
|
|
23
|
+
* user takes over.
|
|
24
|
+
*
|
|
25
|
+
* Theming flows through CSS custom properties on `:host`, so the chart
|
|
26
|
+
* adopts the host page palette without runtime color probing.
|
|
26
27
|
*/
|
|
27
28
|
@customElement('roxy-vedic-kundli')
|
|
28
29
|
export class RoxyVedicKundli extends LitElement {
|
|
29
|
-
static styles = [
|
|
30
|
-
baseStyles,
|
|
31
|
-
css`
|
|
32
|
-
.wrap {
|
|
33
|
-
display: grid;
|
|
34
|
-
gap: var(--roxy-space-md, 1rem);
|
|
35
|
-
}
|
|
36
|
-
.title {
|
|
37
|
-
font-size: var(--roxy-text-lg, 1.125rem);
|
|
38
|
-
font-weight: var(--roxy-weight-bold, 600);
|
|
39
|
-
margin: 0;
|
|
40
|
-
}
|
|
41
|
-
svg {
|
|
42
|
-
display: block;
|
|
43
|
-
width: 100%;
|
|
44
|
-
max-width: 360px;
|
|
45
|
-
margin: 0 auto;
|
|
46
|
-
}
|
|
47
|
-
.line {
|
|
48
|
-
fill: transparent;
|
|
49
|
-
stroke: var(--roxy-border, #e4e4e7);
|
|
50
|
-
}
|
|
51
|
-
.sign-text {
|
|
52
|
-
fill: var(--roxy-muted, #71717a);
|
|
53
|
-
font-size: 9px;
|
|
54
|
-
font-weight: 500;
|
|
55
|
-
font-family: var(--roxy-font-sans);
|
|
56
|
-
}
|
|
57
|
-
.planet-text {
|
|
58
|
-
fill: var(--roxy-fg, #0a0a0a);
|
|
59
|
-
font-size: 10px;
|
|
60
|
-
font-weight: 600;
|
|
61
|
-
font-family: var(--roxy-font-sans);
|
|
62
|
-
}
|
|
63
|
-
.house-num {
|
|
64
|
-
fill: var(--roxy-muted, #71717a);
|
|
65
|
-
font-size: 9px;
|
|
66
|
-
font-weight: 400;
|
|
67
|
-
font-family: var(--roxy-font-sans);
|
|
68
|
-
}
|
|
69
|
-
.lagna-marker {
|
|
70
|
-
fill: var(--roxy-accent-fg, #b45309);
|
|
71
|
-
font-size: 8px;
|
|
72
|
-
font-weight: 700;
|
|
73
|
-
font-family: var(--roxy-font-sans);
|
|
74
|
-
letter-spacing: 0.05em;
|
|
75
|
-
}
|
|
76
|
-
.lagna-bg {
|
|
77
|
-
fill: color-mix(in srgb, var(--roxy-accent, #f59e0b) 12%, transparent);
|
|
78
|
-
stroke: color-mix(in srgb, var(--roxy-accent, #f59e0b) 45%, transparent);
|
|
79
|
-
stroke-width: 0.8;
|
|
80
|
-
}
|
|
81
|
-
`,
|
|
82
|
-
];
|
|
30
|
+
static styles = [baseStyles, kundliStyles];
|
|
83
31
|
|
|
84
32
|
@property({ attribute: false })
|
|
85
33
|
data: BirthChartResponse | null = null;
|
|
86
34
|
|
|
87
35
|
@property({ type: String, reflect: true, attribute: 'chart-style' })
|
|
88
|
-
chartStyle:
|
|
36
|
+
chartStyle: ChartStyle = 'north';
|
|
89
37
|
|
|
90
|
-
private
|
|
91
|
-
if (!this.data?.meta) return
|
|
92
|
-
return
|
|
38
|
+
private viewModel(): KundliViewModel | null {
|
|
39
|
+
if (!this.data?.meta) return null;
|
|
40
|
+
return toKundliViewModel(this.data.meta, 'D1 Rashi');
|
|
93
41
|
}
|
|
94
42
|
|
|
43
|
+
private setStyle = (next: ChartStyle) => {
|
|
44
|
+
this.chartStyle = next;
|
|
45
|
+
};
|
|
46
|
+
|
|
95
47
|
render() {
|
|
96
|
-
|
|
48
|
+
const vm = this.viewModel();
|
|
49
|
+
if (!vm)
|
|
97
50
|
return html`<div class="roxy-empty" role="status">No kundli data</div>`;
|
|
98
|
-
const houses = this.buildHouses();
|
|
99
|
-
const style = this.chartStyle;
|
|
100
|
-
|
|
101
|
-
const frame =
|
|
102
|
-
style === 'north'
|
|
103
|
-
? renderNorthFrame()
|
|
104
|
-
: style === 'east'
|
|
105
|
-
? renderEastFrame()
|
|
106
|
-
: renderSouthFrame();
|
|
107
|
-
const houseGroup =
|
|
108
|
-
style === 'north'
|
|
109
|
-
? renderNorthHouseGroup
|
|
110
|
-
: style === 'east'
|
|
111
|
-
? renderEastHouseGroup
|
|
112
|
-
: renderSouthHouseGroup;
|
|
113
|
-
|
|
114
51
|
return html`<div class="wrap">
|
|
115
|
-
<
|
|
52
|
+
<div class="header">
|
|
53
|
+
<h2 class="title">Vedic kundli</h2>
|
|
54
|
+
${renderKundliStyleTablist(this.chartStyle, this.setStyle)}
|
|
55
|
+
</div>
|
|
116
56
|
<svg
|
|
117
|
-
viewBox="0 0
|
|
57
|
+
viewBox="0 0 400 400"
|
|
58
|
+
preserveAspectRatio="xMidYMid meet"
|
|
118
59
|
role="img"
|
|
119
60
|
aria-label="Vedic birth chart with twelve sign houses"
|
|
120
61
|
>
|
|
121
62
|
<title>Vedic kundli</title>
|
|
122
|
-
${
|
|
123
|
-
${houses.map((h) => houseGroup(h))}
|
|
63
|
+
${renderKundliSvg(vm, this.chartStyle)}
|
|
124
64
|
</svg>
|
|
125
65
|
</div>`;
|
|
126
66
|
}
|