@roxyapi/ui 0.1.1 → 0.1.3
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 +2 -2
- package/LICENSE +21 -0
- package/README.md +505 -0
- package/THEMING.md +24 -7
- package/dist/cdn/components/biorhythm-chart.js +15 -22
- package/dist/cdn/components/biorhythm-chart.js.map +3 -3
- package/dist/cdn/components/compatibility-card.js +36 -34
- package/dist/cdn/components/compatibility-card.js.map +4 -4
- package/dist/cdn/components/dasha-timeline.js +35 -39
- package/dist/cdn/components/dasha-timeline.js.map +4 -4
- package/dist/cdn/components/data.js +6 -6
- package/dist/cdn/components/data.js.map +3 -3
- package/dist/cdn/components/dosha-card.js +13 -13
- package/dist/cdn/components/dosha-card.js.map +2 -2
- package/dist/cdn/components/endpoint-form.js +47 -28
- package/dist/cdn/components/endpoint-form.js.map +3 -3
- package/dist/cdn/components/guna-milan.js +18 -18
- package/dist/cdn/components/guna-milan.js.map +4 -4
- package/dist/cdn/components/hexagram.js +26 -26
- package/dist/cdn/components/hexagram.js.map +3 -3
- package/dist/cdn/components/horoscope-card.js +38 -38
- package/dist/cdn/components/horoscope-card.js.map +3 -3
- package/dist/cdn/components/kp-planets-table.js +10 -10
- package/dist/cdn/components/kp-planets-table.js.map +4 -4
- package/dist/cdn/components/location-search.js +6 -6
- package/dist/cdn/components/location-search.js.map +3 -3
- package/dist/cdn/components/moon-phase.js +21 -21
- package/dist/cdn/components/moon-phase.js.map +4 -4
- package/dist/cdn/components/natal-chart.js +61 -19
- package/dist/cdn/components/natal-chart.js.map +4 -4
- package/dist/cdn/components/numerology-card.js +40 -31
- package/dist/cdn/components/numerology-card.js.map +3 -3
- package/dist/cdn/components/panchang-table.js +25 -25
- package/dist/cdn/components/panchang-table.js.map +4 -4
- package/dist/cdn/components/synastry-chart.js +129 -39
- package/dist/cdn/components/synastry-chart.js.map +4 -4
- package/dist/cdn/components/tarot-card.js +49 -20
- package/dist/cdn/components/tarot-card.js.map +3 -3
- package/dist/cdn/components/tarot-spread.js +43 -27
- package/dist/cdn/components/tarot-spread.js.map +3 -3
- package/dist/cdn/components/vedic-kundli.js +23 -9
- package/dist/cdn/components/vedic-kundli.js.map +3 -3
- package/dist/cdn/roxy-ui.js +560 -350
- package/dist/cdn/roxy-ui.js.map +4 -4
- package/dist/components/biorhythm-chart.d.ts +2 -46
- package/dist/components/biorhythm-chart.d.ts.map +1 -1
- package/dist/components/biorhythm-chart.js +24 -23
- package/dist/components/biorhythm-chart.js.map +2 -2
- package/dist/components/compatibility-card.d.ts +2 -27
- package/dist/components/compatibility-card.d.ts.map +1 -1
- package/dist/components/compatibility-card.js +50 -29
- package/dist/components/compatibility-card.js.map +3 -3
- package/dist/components/dasha-timeline.d.ts +2 -31
- package/dist/components/dasha-timeline.d.ts.map +1 -1
- package/dist/components/dasha-timeline.js +32 -30
- package/dist/components/dasha-timeline.js.map +3 -3
- package/dist/components/data.d.ts +6 -0
- package/dist/components/data.d.ts.map +1 -1
- package/dist/components/data.js +9 -1
- package/dist/components/data.js.map +2 -2
- package/dist/components/dosha-card.d.ts +2 -16
- package/dist/components/dosha-card.d.ts.map +1 -1
- package/dist/components/dosha-card.js +12 -13
- package/dist/components/dosha-card.js.map +2 -2
- package/dist/components/endpoint-form.d.ts +2 -0
- package/dist/components/endpoint-form.d.ts.map +1 -1
- package/dist/components/endpoint-form.js +66 -8
- package/dist/components/endpoint-form.js.map +2 -2
- package/dist/components/guna-milan.d.ts +2 -20
- package/dist/components/guna-milan.d.ts.map +1 -1
- package/dist/components/guna-milan.js +22 -12
- package/dist/components/guna-milan.js.map +3 -3
- package/dist/components/hexagram.d.ts +3 -27
- package/dist/components/hexagram.d.ts.map +1 -1
- package/dist/components/hexagram.js +31 -15
- package/dist/components/hexagram.js.map +2 -2
- package/dist/components/horoscope-card.d.ts +2 -20
- package/dist/components/horoscope-card.d.ts.map +1 -1
- package/dist/components/horoscope-card.js +24 -15
- package/dist/components/horoscope-card.js.map +2 -2
- package/dist/components/kp-planets-table.d.ts +2 -21
- package/dist/components/kp-planets-table.d.ts.map +1 -1
- package/dist/components/kp-planets-table.js +10 -4
- package/dist/components/kp-planets-table.js.map +3 -3
- package/dist/components/location-search.d.ts +3 -11
- package/dist/components/location-search.d.ts.map +1 -1
- package/dist/components/location-search.js +45 -5
- package/dist/components/location-search.js.map +2 -2
- package/dist/components/moon-phase.d.ts +4 -21
- package/dist/components/moon-phase.d.ts.map +1 -1
- package/dist/components/moon-phase.js +17 -4
- package/dist/components/moon-phase.js.map +3 -3
- package/dist/components/natal-chart.d.ts +7 -43
- package/dist/components/natal-chart.d.ts.map +1 -1
- package/dist/components/natal-chart.js +130 -70
- package/dist/components/natal-chart.js.map +3 -3
- package/dist/components/numerology-card.d.ts +5 -37
- package/dist/components/numerology-card.d.ts.map +1 -1
- package/dist/components/numerology-card.js +54 -28
- package/dist/components/numerology-card.js.map +2 -2
- package/dist/components/panchang-table.d.ts +3 -62
- package/dist/components/panchang-table.d.ts.map +1 -1
- package/dist/components/panchang-table.js +62 -32
- package/dist/components/panchang-table.js.map +3 -3
- package/dist/components/synastry-chart.d.ts +9 -28
- package/dist/components/synastry-chart.d.ts.map +1 -1
- package/dist/components/synastry-chart.js +178 -38
- package/dist/components/synastry-chart.js.map +3 -3
- package/dist/components/tarot-card.d.ts +5 -29
- package/dist/components/tarot-card.d.ts.map +1 -1
- package/dist/components/tarot-card.js +59 -20
- package/dist/components/tarot-card.js.map +2 -2
- package/dist/components/tarot-spread.d.ts +2 -24
- package/dist/components/tarot-spread.d.ts.map +1 -1
- package/dist/components/tarot-spread.js +39 -13
- package/dist/components/tarot-spread.js.map +2 -2
- package/dist/components/vedic-kundli.d.ts +3 -23
- package/dist/components/vedic-kundli.d.ts.map +1 -1
- package/dist/components/vedic-kundli.js +25 -13
- package/dist/components/vedic-kundli.js.map +2 -2
- package/dist/index.cjs +1149 -358
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1149 -358
- package/dist/index.js.map +4 -4
- package/dist/manifest.d.ts +49 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/styles/tokens.css +47 -1
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/types.gen.d.ts +27811 -0
- package/dist/types/types.gen.d.ts.map +1 -0
- package/dist/utils/debounce.d.ts +9 -1
- package/dist/utils/debounce.d.ts.map +1 -1
- package/dist/utils/format.d.ts +15 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +9 -1
- package/src/components/biorhythm-chart.ts +39 -84
- package/src/components/compatibility-card.ts +85 -52
- package/src/components/dasha-timeline.ts +55 -73
- package/src/components/data.ts +20 -1
- package/src/components/dosha-card.ts +18 -31
- package/src/components/endpoint-form.ts +79 -11
- package/src/components/guna-milan.ts +16 -34
- package/src/components/hexagram.ts +53 -43
- package/src/components/horoscope-card.ts +51 -39
- package/src/components/kp-planets-table.ts +8 -27
- package/src/components/location-search.ts +45 -20
- package/src/components/moon-phase.ts +28 -25
- package/src/components/natal-chart.ts +129 -84
- package/src/components/numerology-card.ts +87 -79
- package/src/components/panchang-table.ts +40 -78
- package/src/components/synastry-chart.ts +220 -78
- package/src/components/tarot-card.ts +76 -62
- package/src/components/tarot-spread.ts +72 -45
- package/src/components/vedic-kundli.ts +42 -51
- package/src/index.ts +14 -24
- package/src/manifest.ts +366 -0
- package/src/styles/tokens.css +47 -1
- package/src/tokens/index.ts +5 -0
- package/src/types/types.gen.ts +1 -1
- package/src/utils/debounce.ts +23 -4
- package/src/utils/format.ts +57 -0
- package/src/version.ts +2 -0
|
@@ -1,53 +1,16 @@
|
|
|
1
1
|
import { css, html, LitElement, nothing } from 'lit';
|
|
2
2
|
import { customElement, property } from 'lit/decorators.js';
|
|
3
|
+
import type {
|
|
4
|
+
GetBasicPanchangResponse,
|
|
5
|
+
GetDetailedPanchangResponse,
|
|
6
|
+
} from '../types/index.js';
|
|
3
7
|
import { baseStyles } from '../utils/base-styles.js';
|
|
8
|
+
import { formatDate, formatTime, formatTimeRange } from '../utils/format.js';
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
end?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface PanchangData {
|
|
11
|
-
date?: string;
|
|
12
|
-
location?: { name?: string; latitude?: number; longitude?: number };
|
|
13
|
-
vara?: string;
|
|
14
|
-
sunrise?: string;
|
|
15
|
-
sunset?: string;
|
|
16
|
-
moonrise?: string;
|
|
17
|
-
moonset?: string;
|
|
18
|
-
sunSign?: string;
|
|
19
|
-
moonSign?: string;
|
|
20
|
-
sunNakshatra?: string;
|
|
21
|
-
tithi?: string | { name?: string; phase?: string; end?: string };
|
|
22
|
-
nakshatra?: string | { name?: string; lord?: string; end?: string };
|
|
23
|
-
yoga?: string | { name?: string; end?: string };
|
|
24
|
-
karana?: string | { name?: string; end?: string };
|
|
25
|
-
hora?: string;
|
|
26
|
-
rahuKaal?: PanchangTime;
|
|
27
|
-
yamaganda?: PanchangTime;
|
|
28
|
-
gulika?: PanchangTime;
|
|
29
|
-
abhijitMuhurta?: PanchangTime;
|
|
30
|
-
brahmaMuhurta?: PanchangTime;
|
|
31
|
-
vijayaMuhurta?: PanchangTime;
|
|
32
|
-
nishitaMuhurta?: PanchangTime;
|
|
33
|
-
godhuliMuhurta?: PanchangTime;
|
|
34
|
-
pratahSandhya?: PanchangTime;
|
|
35
|
-
sayahnaSandhya?: PanchangTime;
|
|
36
|
-
durMuhurta?: PanchangTime[];
|
|
37
|
-
varjyam?: PanchangTime[];
|
|
38
|
-
amritKalam?: PanchangTime[];
|
|
39
|
-
chandrabalam?: string | string[];
|
|
40
|
-
tarabalam?: string;
|
|
41
|
-
panchaka?: string;
|
|
42
|
-
bhadra?: string;
|
|
43
|
-
sunLongitude?: number;
|
|
44
|
-
moonLongitude?: number;
|
|
45
|
-
}
|
|
10
|
+
type PanchangData = GetBasicPanchangResponse | GetDetailedPanchangResponse;
|
|
11
|
+
type PanchangTime = GetDetailedPanchangResponse['rahuKaal'];
|
|
46
12
|
|
|
47
|
-
/**
|
|
48
|
-
* Panchang table for /vedic-astrology/panchang/{basic,detailed}. Detailed mode
|
|
49
|
-
* renders 15+ muhurtas. Basic mode renders the five elements only.
|
|
50
|
-
*/
|
|
13
|
+
/** Panchang table for /vedic-astrology/panchang/{basic,detailed}. */
|
|
51
14
|
@customElement('roxy-panchang-table')
|
|
52
15
|
export class RoxyPanchangTable extends LitElement {
|
|
53
16
|
static styles = [
|
|
@@ -123,35 +86,40 @@ export class RoxyPanchangTable extends LitElement {
|
|
|
123
86
|
const d = this.data;
|
|
124
87
|
if (!d)
|
|
125
88
|
return html`<div class="roxy-empty" role="status">No panchang data</div>`;
|
|
89
|
+
const detailed = 'sunrise' in d ? d : null;
|
|
126
90
|
|
|
127
|
-
const fivefold = [
|
|
91
|
+
const fivefold: Array<[string, string]> = [
|
|
128
92
|
['Tithi', this.formatPart(d.tithi)],
|
|
129
93
|
['Nakshatra', this.formatPart(d.nakshatra)],
|
|
130
94
|
['Yoga', this.formatPart(d.yoga)],
|
|
131
95
|
['Karana', this.formatPart(d.karana)],
|
|
132
|
-
['Vara', d.vara ?? ''],
|
|
133
96
|
];
|
|
97
|
+
if (detailed) fivefold.push(['Vara', this.formatPart(detailed.vara)]);
|
|
134
98
|
|
|
135
|
-
const muhurtas: Array<[string, PanchangTime | undefined]> =
|
|
136
|
-
[
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
99
|
+
const muhurtas: Array<[string, PanchangTime | undefined]> = detailed
|
|
100
|
+
? [
|
|
101
|
+
['Brahma Muhurta', detailed.brahmaMuhurta],
|
|
102
|
+
['Abhijit Muhurta', detailed.abhijitMuhurta],
|
|
103
|
+
['Vijaya Muhurta', detailed.vijayaMuhurta],
|
|
104
|
+
['Godhuli Muhurta', detailed.godhuliMuhurta],
|
|
105
|
+
['Nishita Muhurta', detailed.nishitaMuhurta],
|
|
106
|
+
['Pratah Sandhya', detailed.pratahSandhya],
|
|
107
|
+
['Sayahna Sandhya', detailed.sayahnaSandhya],
|
|
108
|
+
]
|
|
109
|
+
: [];
|
|
144
110
|
|
|
145
|
-
const inauspicious: Array<[string, PanchangTime | undefined]> =
|
|
146
|
-
[
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
111
|
+
const inauspicious: Array<[string, PanchangTime | undefined]> = detailed
|
|
112
|
+
? [
|
|
113
|
+
['Rahu Kaal', detailed.rahuKaal],
|
|
114
|
+
['Yamaganda', detailed.yamaganda],
|
|
115
|
+
['Gulika', detailed.gulika],
|
|
116
|
+
]
|
|
117
|
+
: [];
|
|
150
118
|
|
|
151
119
|
return html`<div class="wrap" aria-label="Panchang">
|
|
152
120
|
<header class="head">
|
|
153
121
|
<h2 class="title">Panchang</h2>
|
|
154
|
-
<span class="date">${
|
|
122
|
+
<span class="date">${detailed ? formatDate(detailed.date) : ''}</span>
|
|
155
123
|
</header>
|
|
156
124
|
<table>
|
|
157
125
|
<tbody>
|
|
@@ -162,34 +130,34 @@ export class RoxyPanchangTable extends LitElement {
|
|
|
162
130
|
</tr>`,
|
|
163
131
|
)}
|
|
164
132
|
${
|
|
165
|
-
|
|
133
|
+
detailed?.sunrise
|
|
166
134
|
? html`<tr>
|
|
167
135
|
<th>Sunrise</th>
|
|
168
|
-
<td>${
|
|
136
|
+
<td>${formatTime(detailed.sunrise)}</td>
|
|
169
137
|
</tr>`
|
|
170
138
|
: nothing
|
|
171
139
|
}
|
|
172
140
|
${
|
|
173
|
-
|
|
141
|
+
detailed?.sunset
|
|
174
142
|
? html`<tr>
|
|
175
143
|
<th>Sunset</th>
|
|
176
|
-
<td>${
|
|
144
|
+
<td>${formatTime(detailed.sunset)}</td>
|
|
177
145
|
</tr>`
|
|
178
146
|
: nothing
|
|
179
147
|
}
|
|
180
148
|
${
|
|
181
|
-
|
|
149
|
+
detailed?.moonrise
|
|
182
150
|
? html`<tr>
|
|
183
151
|
<th>Moonrise</th>
|
|
184
|
-
<td>${
|
|
152
|
+
<td>${formatTime(detailed.moonrise)}</td>
|
|
185
153
|
</tr>`
|
|
186
154
|
: nothing
|
|
187
155
|
}
|
|
188
156
|
${
|
|
189
|
-
|
|
157
|
+
detailed?.moonset
|
|
190
158
|
? html`<tr>
|
|
191
159
|
<th>Moonset</th>
|
|
192
|
-
<td>${
|
|
160
|
+
<td>${formatTime(detailed.moonset)}</td>
|
|
193
161
|
</tr>`
|
|
194
162
|
: nothing
|
|
195
163
|
}
|
|
@@ -207,7 +175,7 @@ export class RoxyPanchangTable extends LitElement {
|
|
|
207
175
|
.map(
|
|
208
176
|
([k, v]) => html`<tr>
|
|
209
177
|
<th>${k}</th>
|
|
210
|
-
<td>${
|
|
178
|
+
<td>${formatTimeRange(v)}</td>
|
|
211
179
|
</tr>`,
|
|
212
180
|
)}
|
|
213
181
|
</tbody>
|
|
@@ -220,7 +188,7 @@ export class RoxyPanchangTable extends LitElement {
|
|
|
220
188
|
.map(
|
|
221
189
|
([k, v]) => html`<tr>
|
|
222
190
|
<th>${k}</th>
|
|
223
|
-
<td>${
|
|
191
|
+
<td>${formatTimeRange(v)}</td>
|
|
224
192
|
</tr>`,
|
|
225
193
|
)}
|
|
226
194
|
</tbody>
|
|
@@ -252,12 +220,6 @@ export class RoxyPanchangTable extends LitElement {
|
|
|
252
220
|
}
|
|
253
221
|
}
|
|
254
222
|
|
|
255
|
-
function formatRange(t: PanchangTime | undefined): string {
|
|
256
|
-
if (!t) return '';
|
|
257
|
-
if (t.start && t.end) return `${t.start} - ${t.end}`;
|
|
258
|
-
return t.start ?? t.end ?? '';
|
|
259
|
-
}
|
|
260
|
-
|
|
261
223
|
declare global {
|
|
262
224
|
interface HTMLElementTagNameMap {
|
|
263
225
|
'roxy-panchang-table': RoxyPanchangTable;
|
|
@@ -1,41 +1,26 @@
|
|
|
1
1
|
import { css, html, LitElement, nothing, svg } from 'lit';
|
|
2
2
|
import { customElement, property } from 'lit/decorators.js';
|
|
3
3
|
import { PLANET_GLYPH, SIGN_GLYPH } from '../tokens/index.js';
|
|
4
|
+
import type {
|
|
5
|
+
CalculateSynastryResponse,
|
|
6
|
+
NatalChartResponse,
|
|
7
|
+
} from '../types/index.js';
|
|
4
8
|
import { baseStyles } from '../utils/base-styles.js';
|
|
5
9
|
import { polarToCartesian } from '../utils/degree.js';
|
|
10
|
+
import { formatNumber } from '../utils/format.js';
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
planet?: string;
|
|
10
|
-
longitude?: number;
|
|
11
|
-
degree?: number;
|
|
12
|
-
sign?: string;
|
|
13
|
-
}
|
|
12
|
+
type PlanetEntry = NatalChartResponse['planets'][number];
|
|
13
|
+
type InterAspect = CalculateSynastryResponse['interAspects'][number];
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface SynastryData {
|
|
25
|
-
person1?: {
|
|
26
|
-
planets?: PlanetEntry[] | Record<string, PlanetEntry>;
|
|
27
|
-
name?: string;
|
|
28
|
-
};
|
|
29
|
-
person2?: {
|
|
30
|
-
planets?: PlanetEntry[] | Record<string, PlanetEntry>;
|
|
31
|
-
name?: string;
|
|
32
|
-
};
|
|
33
|
-
compatibilityScore?: number;
|
|
34
|
-
summary?: string;
|
|
35
|
-
interAspects?: InterAspect[];
|
|
36
|
-
strengths?: string[];
|
|
37
|
-
challenges?: string[];
|
|
38
|
-
}
|
|
15
|
+
// TODO(spec): /astrology/synastry does not expose person1/person2 planet
|
|
16
|
+
// positions, but the wheel needs them to plot the dual chart. The preview
|
|
17
|
+
// injects them via scripts/refresh-samples.ts; production callers see an
|
|
18
|
+
// empty wheel. Either add `planets` to the CalculateSynastry response or
|
|
19
|
+
// document that callers must merge in their own natal-chart payloads.
|
|
20
|
+
type SynastryWithPlanets = CalculateSynastryResponse & {
|
|
21
|
+
person1?: { planets?: PlanetEntry[] };
|
|
22
|
+
person2?: { planets?: PlanetEntry[] };
|
|
23
|
+
};
|
|
39
24
|
|
|
40
25
|
const SIZE = 360;
|
|
41
26
|
const CENTER = SIZE / 2;
|
|
@@ -104,6 +89,42 @@ export class RoxySynastryChart extends LitElement {
|
|
|
104
89
|
font-weight: 600;
|
|
105
90
|
font-size: 13px;
|
|
106
91
|
}
|
|
92
|
+
.aspect {
|
|
93
|
+
stroke-width: 0.8;
|
|
94
|
+
fill: none;
|
|
95
|
+
opacity: 0.5;
|
|
96
|
+
}
|
|
97
|
+
.aspect-trine,
|
|
98
|
+
.aspect-sextile {
|
|
99
|
+
stroke: var(--roxy-success, #16a34a);
|
|
100
|
+
}
|
|
101
|
+
.aspect-square,
|
|
102
|
+
.aspect-opposition {
|
|
103
|
+
stroke: var(--roxy-danger, #dc2626);
|
|
104
|
+
}
|
|
105
|
+
.aspect-conjunction {
|
|
106
|
+
stroke: var(--roxy-accent-fg, #b45309);
|
|
107
|
+
}
|
|
108
|
+
.aspect-other {
|
|
109
|
+
stroke: var(--roxy-muted, #71717a);
|
|
110
|
+
opacity: 0.35;
|
|
111
|
+
}
|
|
112
|
+
.legend-row {
|
|
113
|
+
display: flex;
|
|
114
|
+
flex-wrap: wrap;
|
|
115
|
+
gap: var(--roxy-space-md, 1rem);
|
|
116
|
+
font-size: var(--roxy-text-xs, 0.75rem);
|
|
117
|
+
color: var(--roxy-muted, #71717a);
|
|
118
|
+
margin-top: calc(var(--roxy-space-xs, 0.25rem) * -1);
|
|
119
|
+
}
|
|
120
|
+
.legend-row .swatch {
|
|
121
|
+
display: inline-block;
|
|
122
|
+
width: 8px;
|
|
123
|
+
height: 8px;
|
|
124
|
+
border-radius: 50%;
|
|
125
|
+
margin-right: 4px;
|
|
126
|
+
vertical-align: middle;
|
|
127
|
+
}
|
|
107
128
|
|
|
108
129
|
.summary {
|
|
109
130
|
margin: 0;
|
|
@@ -151,24 +172,101 @@ export class RoxySynastryChart extends LitElement {
|
|
|
151
172
|
padding-left: var(--roxy-space-md, 1rem);
|
|
152
173
|
font-size: var(--roxy-text-sm, 0.875rem);
|
|
153
174
|
}
|
|
175
|
+
|
|
176
|
+
.missing-planets {
|
|
177
|
+
background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 8%, transparent);
|
|
178
|
+
border: 1px solid var(--roxy-border, #e4e4e7);
|
|
179
|
+
border-radius: var(--roxy-radius-md, 8px);
|
|
180
|
+
padding: var(--roxy-space-md, 1rem);
|
|
181
|
+
color: var(--roxy-fg, #0a0a0a);
|
|
182
|
+
font-size: var(--roxy-text-sm, 0.875rem);
|
|
183
|
+
line-height: 1.5;
|
|
184
|
+
}
|
|
185
|
+
.missing-planets code {
|
|
186
|
+
font-family: var(--roxy-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
187
|
+
font-size: 0.95em;
|
|
188
|
+
background: color-mix(in srgb, var(--roxy-fg, #0a0a0a) 6%, transparent);
|
|
189
|
+
padding: 0 4px;
|
|
190
|
+
border-radius: 4px;
|
|
191
|
+
}
|
|
154
192
|
`,
|
|
155
193
|
];
|
|
156
194
|
|
|
157
195
|
@property({ attribute: false })
|
|
158
|
-
data:
|
|
196
|
+
data: SynastryWithPlanets | null = null;
|
|
159
197
|
|
|
160
198
|
render() {
|
|
161
199
|
if (!this.data)
|
|
162
200
|
return html`<div class="roxy-empty" role="status">No synastry data</div>`;
|
|
163
|
-
const {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
201
|
+
const { person1, person2, compatibilityScore, analysis } = this.data;
|
|
202
|
+
const interAspects = this.data.interAspects ?? [];
|
|
203
|
+
const p1Planets = person1?.planets ?? [];
|
|
204
|
+
const p2Planets = person2?.planets ?? [];
|
|
205
|
+
|
|
206
|
+
const score =
|
|
207
|
+
typeof compatibilityScore === 'number'
|
|
208
|
+
? Math.round(compatibilityScore)
|
|
209
|
+
: undefined;
|
|
210
|
+
const summaryText = analysis?.overall;
|
|
211
|
+
const strengths = analysis?.strengths ?? [];
|
|
212
|
+
const challenges = analysis?.challenges ?? [];
|
|
213
|
+
|
|
214
|
+
// /astrology/synastry does not return per-person planet positions, so the
|
|
215
|
+
// dual-wheel cannot be drawn from a bare synastry response. Surface this
|
|
216
|
+
// explicitly instead of rendering a blank wheel; keep the inter-aspects
|
|
217
|
+
// table when it is present so callers still get useful output.
|
|
218
|
+
const hasPlanets = p1Planets.length > 0 && p2Planets.length > 0;
|
|
219
|
+
if (!hasPlanets) {
|
|
220
|
+
return html`<div
|
|
221
|
+
class="wrap"
|
|
222
|
+
aria-label="Synastry compatibility chart"
|
|
223
|
+
>
|
|
224
|
+
<div class="head">
|
|
225
|
+
<h2 class="title">Synastry</h2>
|
|
226
|
+
${
|
|
227
|
+
typeof score === 'number'
|
|
228
|
+
? html`<span class="score" aria-label=${`Score ${score} of 100`}
|
|
229
|
+
>${score} / 100</span
|
|
230
|
+
>`
|
|
231
|
+
: nothing
|
|
232
|
+
}
|
|
233
|
+
</div>
|
|
234
|
+
<div class="missing-planets" role="status">
|
|
235
|
+
Synastry response missing planet positions. Pass
|
|
236
|
+
<code>data</code> with <code>person1.planets</code> and
|
|
237
|
+
<code>person2.planets</code> arrays from the natal-chart endpoint, or
|
|
238
|
+
use the <code><roxy-data></code> fallback.
|
|
239
|
+
</div>
|
|
240
|
+
${summaryText ? html`<p class="summary">${summaryText}</p>` : nothing}
|
|
241
|
+
${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing}
|
|
242
|
+
${
|
|
243
|
+
strengths.length > 0 || challenges.length > 0
|
|
244
|
+
? html`<div class="lists">
|
|
245
|
+
${
|
|
246
|
+
strengths.length
|
|
247
|
+
? html`<div>
|
|
248
|
+
<h3>Strengths</h3>
|
|
249
|
+
<ul>
|
|
250
|
+
${strengths.map((s) => html`<li>${s}</li>`)}
|
|
251
|
+
</ul>
|
|
252
|
+
</div>`
|
|
253
|
+
: nothing
|
|
254
|
+
}
|
|
255
|
+
${
|
|
256
|
+
challenges.length
|
|
257
|
+
? html`<div>
|
|
258
|
+
<h3>Challenges</h3>
|
|
259
|
+
<ul>
|
|
260
|
+
${challenges.map((s) => html`<li>${s}</li>`)}
|
|
261
|
+
</ul>
|
|
262
|
+
</div>`
|
|
263
|
+
: nothing
|
|
264
|
+
}
|
|
265
|
+
</div>`
|
|
266
|
+
: nothing
|
|
267
|
+
}
|
|
268
|
+
</div>`;
|
|
269
|
+
}
|
|
172
270
|
|
|
173
271
|
return html`<div
|
|
174
272
|
class="wrap"
|
|
@@ -177,9 +275,9 @@ export class RoxySynastryChart extends LitElement {
|
|
|
177
275
|
<div class="head">
|
|
178
276
|
<h2 class="title">Synastry</h2>
|
|
179
277
|
${
|
|
180
|
-
typeof
|
|
181
|
-
? html`<span class="score" aria-label=${`Score ${
|
|
182
|
-
>${
|
|
278
|
+
typeof score === 'number'
|
|
279
|
+
? html`<span class="score" aria-label=${`Score ${score} of 100`}
|
|
280
|
+
>${score} / 100</span
|
|
183
281
|
>`
|
|
184
282
|
: nothing
|
|
185
283
|
}
|
|
@@ -212,30 +310,36 @@ export class RoxySynastryChart extends LitElement {
|
|
|
212
310
|
stroke-width="0.6"
|
|
213
311
|
/>
|
|
214
312
|
${this.renderSpokes()} ${this.renderSigns()}
|
|
313
|
+
${this.renderInterAspectLines(p1Planets, p2Planets, interAspects)}
|
|
215
314
|
${this.renderRing(p1Planets, P1_R, 'p1')} ${this.renderRing(p2Planets, P2_R, 'p2')}
|
|
216
315
|
</svg>
|
|
217
|
-
|
|
316
|
+
<div class="legend-row">
|
|
317
|
+
<span><span class="swatch" style="background: var(--roxy-accent)"></span>Person 1</span>
|
|
318
|
+
<span><span class="swatch" style="background: var(--roxy-info)"></span>Person 2</span>
|
|
319
|
+
<span><span class="swatch" style="background: var(--roxy-success)"></span>harmonious</span>
|
|
320
|
+
<span><span class="swatch" style="background: var(--roxy-danger)"></span>challenging</span>
|
|
321
|
+
</div>
|
|
322
|
+
${summaryText ? html`<p class="summary">${summaryText}</p>` : nothing}
|
|
218
323
|
${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing}
|
|
219
324
|
${
|
|
220
|
-
|
|
221
|
-
(this.data.challenges?.length ?? 0) > 0
|
|
325
|
+
strengths.length > 0 || challenges.length > 0
|
|
222
326
|
? html`<div class="lists">
|
|
223
327
|
${
|
|
224
|
-
|
|
328
|
+
strengths.length
|
|
225
329
|
? html`<div>
|
|
226
330
|
<h3>Strengths</h3>
|
|
227
331
|
<ul>
|
|
228
|
-
${
|
|
332
|
+
${strengths.map((s) => html`<li>${s}</li>`)}
|
|
229
333
|
</ul>
|
|
230
334
|
</div>`
|
|
231
335
|
: nothing
|
|
232
336
|
}
|
|
233
337
|
${
|
|
234
|
-
|
|
338
|
+
challenges.length
|
|
235
339
|
? html`<div>
|
|
236
340
|
<h3>Challenges</h3>
|
|
237
341
|
<ul>
|
|
238
|
-
${
|
|
342
|
+
${challenges.map((s) => html`<li>${s}</li>`)}
|
|
239
343
|
</ul>
|
|
240
344
|
</div>`
|
|
241
345
|
: nothing
|
|
@@ -246,17 +350,13 @@ export class RoxySynastryChart extends LitElement {
|
|
|
246
350
|
</div>`;
|
|
247
351
|
}
|
|
248
352
|
|
|
249
|
-
private
|
|
250
|
-
|
|
251
|
-
) {
|
|
252
|
-
if (!p) return [];
|
|
253
|
-
if (Array.isArray(p)) return p;
|
|
254
|
-
return Object.entries(p).map(([name, e]) => ({ ...e, name }));
|
|
353
|
+
private toAngle(longitude: number): number {
|
|
354
|
+
return 180 - longitude;
|
|
255
355
|
}
|
|
256
356
|
|
|
257
357
|
private renderSpokes() {
|
|
258
358
|
return Array.from({ length: 12 }, (_, i) => {
|
|
259
|
-
const angle = i * 30
|
|
359
|
+
const angle = this.toAngle(i * 30);
|
|
260
360
|
const start = polarToCartesian(CENTER, CENTER, P2_R - 14, angle);
|
|
261
361
|
const end = polarToCartesian(CENTER, CENTER, OUTER_R, angle);
|
|
262
362
|
return svg`<line class="wheel-line" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width="0.6" />`;
|
|
@@ -279,7 +379,7 @@ export class RoxySynastryChart extends LitElement {
|
|
|
279
379
|
'Pisces',
|
|
280
380
|
];
|
|
281
381
|
return order.map((s, i) => {
|
|
282
|
-
const angle = i * 30 + 15
|
|
382
|
+
const angle = this.toAngle(i * 30 + 15);
|
|
283
383
|
const pos = polarToCartesian(CENTER, CENTER, SIGN_R, angle);
|
|
284
384
|
return svg`<text class="sign" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${SIGN_GLYPH[s]}</text>`;
|
|
285
385
|
});
|
|
@@ -287,17 +387,44 @@ export class RoxySynastryChart extends LitElement {
|
|
|
287
387
|
|
|
288
388
|
private renderRing(planets: PlanetEntry[], radius: number, cls: string) {
|
|
289
389
|
return planets.map((p) => {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
390
|
+
if (!Number.isFinite(p.longitude)) return nothing;
|
|
391
|
+
const pos = polarToCartesian(
|
|
392
|
+
CENTER,
|
|
393
|
+
CENTER,
|
|
394
|
+
radius,
|
|
395
|
+
this.toAngle(p.longitude),
|
|
396
|
+
);
|
|
397
|
+
const glyph = PLANET_GLYPH[capitalize(p.name)] ?? p.name.slice(0, 2);
|
|
398
|
+
return svg`<text class=${cls} x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central"><title>${p.name}</title>${glyph}</text>`;
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
private renderInterAspectLines(
|
|
403
|
+
p1: PlanetEntry[],
|
|
404
|
+
p2: PlanetEntry[],
|
|
405
|
+
aspects: InterAspect[],
|
|
406
|
+
) {
|
|
407
|
+
const longitudeOf = (
|
|
408
|
+
list: PlanetEntry[],
|
|
409
|
+
name: string,
|
|
410
|
+
): number | undefined => {
|
|
411
|
+
const target = capitalize(name);
|
|
412
|
+
for (const p of list) {
|
|
413
|
+
if (capitalize(p.name) !== target) continue;
|
|
414
|
+
if (typeof p.longitude === 'number') return p.longitude;
|
|
415
|
+
}
|
|
416
|
+
return undefined;
|
|
417
|
+
};
|
|
418
|
+
return aspects.map((a) => {
|
|
419
|
+
const l1 = longitudeOf(p1, a.planet1);
|
|
420
|
+
const l2 = longitudeOf(p2, a.planet2);
|
|
421
|
+
if (l1 === undefined || l2 === undefined) return nothing;
|
|
422
|
+
const out = polarToCartesian(CENTER, CENTER, P1_R - 12, this.toAngle(l1));
|
|
423
|
+
const inn = polarToCartesian(CENTER, CENTER, P2_R + 8, this.toAngle(l2));
|
|
424
|
+
const aspectName = normalizeAspect(a);
|
|
425
|
+
const cls = ASPECT_CLASS[aspectName] ?? 'aspect-other';
|
|
426
|
+
const orbLabel = formatNumber(a.orb, 1);
|
|
427
|
+
return svg`<line class=${`aspect ${cls}`} x1=${out.x} y1=${out.y} x2=${inn.x} y2=${inn.y}><title>${a.planet1} ${aspectName} ${a.planet2}${orbLabel ? ` (orb ${orbLabel}°)` : ''}</title></line>`;
|
|
301
428
|
});
|
|
302
429
|
}
|
|
303
430
|
|
|
@@ -313,15 +440,13 @@ export class RoxySynastryChart extends LitElement {
|
|
|
313
440
|
</tr>
|
|
314
441
|
</thead>
|
|
315
442
|
<tbody>
|
|
316
|
-
${aspects.slice(0,
|
|
443
|
+
${aspects.slice(0, 12).map(
|
|
317
444
|
(a) => html`<tr>
|
|
318
|
-
<td>${a.planet1
|
|
319
|
-
<td>${a.planet2
|
|
320
|
-
<td>${a
|
|
321
|
-
<td class="orb">
|
|
322
|
-
|
|
323
|
-
</td>
|
|
324
|
-
<td>${a.strength ?? ''}</td>
|
|
445
|
+
<td>${a.planet1}</td>
|
|
446
|
+
<td>${a.planet2}</td>
|
|
447
|
+
<td>${normalizeAspect(a) || ''}</td>
|
|
448
|
+
<td class="orb">${formatNumber(a.orb, 1)}</td>
|
|
449
|
+
<td>${formatStrength(a.strength)}</td>
|
|
325
450
|
</tr>`,
|
|
326
451
|
)}
|
|
327
452
|
</tbody>
|
|
@@ -334,6 +459,23 @@ function capitalize(s: string): string {
|
|
|
334
459
|
return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
|
|
335
460
|
}
|
|
336
461
|
|
|
462
|
+
const ASPECT_CLASS: Record<string, string> = {
|
|
463
|
+
conjunction: 'aspect-conjunction',
|
|
464
|
+
sextile: 'aspect-sextile',
|
|
465
|
+
square: 'aspect-square',
|
|
466
|
+
trine: 'aspect-trine',
|
|
467
|
+
opposition: 'aspect-opposition',
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
function normalizeAspect(a: InterAspect): string {
|
|
471
|
+
return (a.type ?? '').toLowerCase().replace(/_/g, '-');
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function formatStrength(s: number | undefined): string {
|
|
475
|
+
if (typeof s === 'number') return Math.round(s).toString();
|
|
476
|
+
return '';
|
|
477
|
+
}
|
|
478
|
+
|
|
337
479
|
declare global {
|
|
338
480
|
interface HTMLElementTagNameMap {
|
|
339
481
|
'roxy-synastry-chart': RoxySynastryChart;
|