@roxyapi/ui 0.3.1 → 0.4.1
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 +34 -7
- package/README.md +145 -26
- 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/manifest.json +23 -23
- 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
|
@@ -68,6 +68,13 @@ export class RoxyDashaTimeline extends LitElement {
|
|
|
68
68
|
color: var(--roxy-fg, #0a0a0a);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
.balance {
|
|
72
|
+
font-size: var(--roxy-text-sm, 0.875rem);
|
|
73
|
+
color: var(--roxy-muted, #71717a);
|
|
74
|
+
border-left: 2px solid var(--roxy-border, #e4e4e7);
|
|
75
|
+
padding-left: var(--roxy-space-sm, 0.5rem);
|
|
76
|
+
margin: 0;
|
|
77
|
+
}
|
|
71
78
|
.timeline {
|
|
72
79
|
display: grid;
|
|
73
80
|
gap: var(--roxy-space-xs, 0.25rem);
|
|
@@ -79,26 +86,69 @@ export class RoxyDashaTimeline extends LitElement {
|
|
|
79
86
|
align-items: center;
|
|
80
87
|
font-size: var(--roxy-text-sm, 0.875rem);
|
|
81
88
|
}
|
|
89
|
+
.bar.now strong {
|
|
90
|
+
color: var(--roxy-accent-fg, #b45309);
|
|
91
|
+
}
|
|
92
|
+
.now-badge {
|
|
93
|
+
display: inline-block;
|
|
94
|
+
margin-left: 0.4em;
|
|
95
|
+
font-size: var(--roxy-text-xs, 0.75rem);
|
|
96
|
+
font-weight: var(--roxy-weight-bold, 600);
|
|
97
|
+
color: var(--roxy-accent-fg, #b45309);
|
|
98
|
+
text-transform: uppercase;
|
|
99
|
+
letter-spacing: 0.06em;
|
|
100
|
+
}
|
|
82
101
|
.bar-track {
|
|
102
|
+
position: relative;
|
|
83
103
|
height: 14px;
|
|
84
104
|
background: var(--roxy-border, #e4e4e7);
|
|
85
105
|
border-radius: var(--roxy-radius-full, 9999px);
|
|
86
106
|
overflow: hidden;
|
|
87
107
|
}
|
|
88
|
-
.bar-
|
|
108
|
+
.bar-fill {
|
|
89
109
|
display: block;
|
|
90
110
|
height: 100%;
|
|
91
111
|
background: var(--roxy-accent, #f59e0b);
|
|
112
|
+
opacity: 0.45;
|
|
92
113
|
transition:
|
|
93
114
|
width var(--roxy-motion-duration, 200ms)
|
|
94
115
|
var(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
95
116
|
}
|
|
117
|
+
.bar-now .bar-fill {
|
|
118
|
+
opacity: 1;
|
|
119
|
+
}
|
|
120
|
+
.bar-progress {
|
|
121
|
+
position: absolute;
|
|
122
|
+
top: -2px;
|
|
123
|
+
bottom: -2px;
|
|
124
|
+
width: 2px;
|
|
125
|
+
background: var(--roxy-accent-fg, #b45309);
|
|
126
|
+
border-radius: 2px;
|
|
127
|
+
box-shadow: 0 0 0 2px
|
|
128
|
+
color-mix(in srgb, var(--roxy-accent, #f59e0b) 35%, transparent);
|
|
129
|
+
}
|
|
96
130
|
.dates {
|
|
97
131
|
color: var(--roxy-muted, #71717a);
|
|
98
132
|
font-size: var(--roxy-text-xs, 0.75rem);
|
|
99
133
|
font-variant-numeric: tabular-nums;
|
|
100
134
|
text-align: right;
|
|
101
135
|
}
|
|
136
|
+
details.interp {
|
|
137
|
+
border: 1px solid var(--roxy-border, #e4e4e7);
|
|
138
|
+
border-radius: var(--roxy-radius-md, 8px);
|
|
139
|
+
padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
|
|
140
|
+
background: var(--roxy-bg, #fff);
|
|
141
|
+
}
|
|
142
|
+
details.interp summary {
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
font-size: var(--roxy-text-sm, 0.875rem);
|
|
145
|
+
font-weight: var(--roxy-weight-bold, 600);
|
|
146
|
+
}
|
|
147
|
+
details.interp p {
|
|
148
|
+
margin: var(--roxy-space-sm, 0.5rem) 0 0;
|
|
149
|
+
font-size: var(--roxy-text-sm, 0.875rem);
|
|
150
|
+
color: var(--roxy-muted, #71717a);
|
|
151
|
+
}
|
|
102
152
|
`,
|
|
103
153
|
];
|
|
104
154
|
|
|
@@ -139,6 +189,7 @@ export class RoxyDashaTimeline extends LitElement {
|
|
|
139
189
|
}
|
|
140
190
|
</header>
|
|
141
191
|
|
|
192
|
+
${this.renderBirthBalance(d)}
|
|
142
193
|
${this.period === 'current' ? this.renderCurrent(d) : nothing}
|
|
143
194
|
${
|
|
144
195
|
periods.length > 0
|
|
@@ -147,9 +198,37 @@ export class RoxyDashaTimeline extends LitElement {
|
|
|
147
198
|
</div>`
|
|
148
199
|
: nothing
|
|
149
200
|
}
|
|
201
|
+
${this.renderActiveInterpretation(periods)}
|
|
150
202
|
</div>`;
|
|
151
203
|
}
|
|
152
204
|
|
|
205
|
+
private renderBirthBalance(d: DashaData) {
|
|
206
|
+
if (!('birthDashaBalance' in d) || !d.birthDashaBalance) return nothing;
|
|
207
|
+
const b = d.birthDashaBalance;
|
|
208
|
+
const lord = 'nakshatraLord' in d && d.nakshatraLord ? d.nakshatraLord : '';
|
|
209
|
+
const yrs = b.years ?? 0;
|
|
210
|
+
const mo = b.months ?? 0;
|
|
211
|
+
const da = b.days ?? 0;
|
|
212
|
+
const parts: string[] = [];
|
|
213
|
+
if (yrs) parts.push(`${yrs}y`);
|
|
214
|
+
if (mo) parts.push(`${mo}m`);
|
|
215
|
+
if (da) parts.push(`${da}d`);
|
|
216
|
+
const remaining = parts.length ? parts.join(' ') : '0d';
|
|
217
|
+
return html`<p class="balance">
|
|
218
|
+
Birth dasha balance: ${remaining} of
|
|
219
|
+
${lord ? html`<strong>${lord}</strong>` : 'the opening mahadasha'} remained at birth.
|
|
220
|
+
</p>`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private renderActiveInterpretation(periods: DashaPeriod[]) {
|
|
224
|
+
const active = periods.find((p) => this.isCurrent(p));
|
|
225
|
+
if (!active?.interpretation) return nothing;
|
|
226
|
+
return html`<details class="interp">
|
|
227
|
+
<summary>${active.planet} mahadasha interpretation</summary>
|
|
228
|
+
<p>${active.interpretation}</p>
|
|
229
|
+
</details>`;
|
|
230
|
+
}
|
|
231
|
+
|
|
153
232
|
private renderCurrent(d: DashaData) {
|
|
154
233
|
if (!('mahadasha' in d)) return nothing;
|
|
155
234
|
return html`<div class="current">
|
|
@@ -201,12 +280,64 @@ export class RoxyDashaTimeline extends LitElement {
|
|
|
201
280
|
return [];
|
|
202
281
|
}
|
|
203
282
|
|
|
283
|
+
/** True when the current wall-clock time falls between the period's start and end. */
|
|
284
|
+
private isCurrent(p: DashaPeriod): boolean {
|
|
285
|
+
if (!p.startDate || !p.endDate) return false;
|
|
286
|
+
const now = Date.now();
|
|
287
|
+
const start = Date.parse(p.startDate);
|
|
288
|
+
const end = Date.parse(p.endDate);
|
|
289
|
+
if (Number.isNaN(start) || Number.isNaN(end)) return false;
|
|
290
|
+
return now >= start && now < end;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Fractional progress (0..1) through a period at the current time. Used to
|
|
295
|
+
* draw a vertical "now" marker inside the active bar. Returns -1 outside the
|
|
296
|
+
* period so the caller can skip the marker.
|
|
297
|
+
*/
|
|
298
|
+
private progressIn(p: DashaPeriod): number {
|
|
299
|
+
if (!p.startDate || !p.endDate) return -1;
|
|
300
|
+
const start = Date.parse(p.startDate);
|
|
301
|
+
const end = Date.parse(p.endDate);
|
|
302
|
+
const now = Date.now();
|
|
303
|
+
if (
|
|
304
|
+
Number.isNaN(start) ||
|
|
305
|
+
Number.isNaN(end) ||
|
|
306
|
+
now < start ||
|
|
307
|
+
now >= end ||
|
|
308
|
+
end <= start
|
|
309
|
+
) {
|
|
310
|
+
return -1;
|
|
311
|
+
}
|
|
312
|
+
return (now - start) / (end - start);
|
|
313
|
+
}
|
|
314
|
+
|
|
204
315
|
private renderBar(p: DashaPeriod, max: number) {
|
|
205
316
|
const years = p.durationYears;
|
|
206
317
|
const width = max > 0 ? (years / max) * 100 : 0;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
318
|
+
const current = this.isCurrent(p);
|
|
319
|
+
const progress = current ? this.progressIn(p) : -1;
|
|
320
|
+
const trackClass = current ? 'bar-track bar-now' : 'bar-track';
|
|
321
|
+
return html`<div
|
|
322
|
+
class=${current ? 'bar now' : 'bar'}
|
|
323
|
+
role="listitem"
|
|
324
|
+
aria-current=${current ? 'time' : 'false'}
|
|
325
|
+
>
|
|
326
|
+
<span>
|
|
327
|
+
<strong>${p.planet}</strong>${current ? html`<span class="now-badge">Now</span>` : nothing}
|
|
328
|
+
</span>
|
|
329
|
+
<span class=${trackClass}>
|
|
330
|
+
<span class="bar-fill" style="width: ${width}%"></span>
|
|
331
|
+
${
|
|
332
|
+
progress >= 0
|
|
333
|
+
? html`<span
|
|
334
|
+
class="bar-progress"
|
|
335
|
+
style="left: ${progress * width}%"
|
|
336
|
+
aria-hidden="true"
|
|
337
|
+
></span>`
|
|
338
|
+
: nothing
|
|
339
|
+
}
|
|
340
|
+
</span>
|
|
210
341
|
<span class="dates">
|
|
211
342
|
${p.startDate ? formatYear(p.startDate) : ''}
|
|
212
343
|
${p.endDate ? html`- ${formatYear(p.endDate)}` : ''}
|
|
@@ -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(' ');
|