@roxyapi/ui 0.1.2 → 0.2.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 +6 -0
- package/README.md +327 -14
- package/THEMING.md +24 -7
- package/dist/cdn/components/ashtakavarga-grid.js +349 -0
- package/dist/cdn/components/ashtakavarga-grid.js.map +7 -0
- package/dist/cdn/components/biorhythm-chart.js +15 -22
- package/dist/cdn/components/biorhythm-chart.js.map +3 -3
- package/dist/cdn/components/choghadiya-grid.js +239 -0
- package/dist/cdn/components/choghadiya-grid.js.map +7 -0
- 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 +9 -9
- package/dist/cdn/components/data.js.map +4 -4
- package/dist/cdn/components/divisional-chart.js +279 -0
- package/dist/cdn/components/divisional-chart.js.map +7 -0
- package/dist/cdn/components/dosha-card.js +49 -49
- package/dist/cdn/components/dosha-card.js.map +3 -3
- package/dist/cdn/components/endpoint-form.js +47 -28
- package/dist/cdn/components/endpoint-form.js.map +4 -4
- package/dist/cdn/components/guna-milan.js +66 -24
- 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 +47 -40
- package/dist/cdn/components/horoscope-card.js.map +4 -4
- 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 +18 -18
- package/dist/cdn/components/moon-phase.js.map +4 -4
- package/dist/cdn/components/natal-chart.js +240 -24
- 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 +4 -4
- package/dist/cdn/components/panchang-table.js +30 -30
- package/dist/cdn/components/panchang-table.js.map +4 -4
- package/dist/cdn/components/shadbala-table.js +312 -0
- package/dist/cdn/components/shadbala-table.js.map +7 -0
- 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/transits-table.js +391 -0
- package/dist/cdn/components/transits-table.js.map +7 -0
- package/dist/cdn/components/vedic-kundli.js +63 -27
- package/dist/cdn/components/vedic-kundli.js.map +4 -4
- package/dist/cdn/components/yoga-list.js +334 -0
- package/dist/cdn/components/yoga-list.js.map +7 -0
- package/dist/cdn/roxy-ui.js +2104 -544
- package/dist/cdn/roxy-ui.js.map +4 -4
- package/dist/components/ashtakavarga-grid.d.ts +26 -0
- package/dist/components/ashtakavarga-grid.d.ts.map +1 -0
- package/dist/components/ashtakavarga-grid.js +457 -0
- package/dist/components/ashtakavarga-grid.js.map +7 -0
- 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/choghadiya-grid.d.ts +19 -0
- package/dist/components/choghadiya-grid.d.ts.map +1 -0
- package/dist/components/choghadiya-grid.js +304 -0
- package/dist/components/choghadiya-grid.js.map +7 -0
- 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 +11 -7
- package/dist/components/data.d.ts.map +1 -1
- package/dist/components/data.js +16 -6
- package/dist/components/data.js.map +3 -3
- package/dist/components/divisional-chart.d.ts +20 -0
- package/dist/components/divisional-chart.d.ts.map +1 -0
- package/dist/components/divisional-chart.js +471 -0
- package/dist/components/divisional-chart.js.map +7 -0
- 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 +45 -43
- 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 +71 -11
- package/dist/components/endpoint-form.js.map +3 -3
- 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 +79 -20
- package/dist/components/guna-milan.js.map +4 -4
- package/dist/components/hexagram.d.ts +3 -27
- package/dist/components/hexagram.d.ts.map +1 -1
- package/dist/components/hexagram.js +48 -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 +54 -18
- package/dist/components/horoscope-card.js.map +3 -3
- 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 +5 -14
- 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 +34 -4
- package/dist/components/moon-phase.js.map +3 -3
- package/dist/components/natal-chart.d.ts +9 -43
- package/dist/components/natal-chart.d.ts.map +1 -1
- package/dist/components/natal-chart.js +346 -79
- 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 +58 -30
- package/dist/components/numerology-card.js.map +3 -3
- 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/shadbala-table.d.ts +18 -0
- package/dist/components/shadbala-table.d.ts.map +1 -0
- package/dist/components/shadbala-table.js +400 -0
- package/dist/components/shadbala-table.js.map +7 -0
- 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 +201 -56
- 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/transits-table.d.ts +21 -0
- package/dist/components/transits-table.d.ts.map +1 -0
- package/dist/components/transits-table.js +515 -0
- package/dist/components/transits-table.js.map +7 -0
- package/dist/components/vedic-kundli.d.ts +5 -28
- package/dist/components/vedic-kundli.d.ts.map +1 -1
- package/dist/components/vedic-kundli.js +147 -83
- package/dist/components/vedic-kundli.js.map +3 -3
- package/dist/components/yoga-list.d.ts +29 -0
- package/dist/components/yoga-list.d.ts.map +1 -0
- package/dist/components/yoga-list.js +389 -0
- package/dist/components/yoga-list.js.map +7 -0
- package/dist/index.cjs +3693 -1180
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3709 -1196
- package/dist/index.js.map +4 -4
- package/dist/manifest.d.ts +43 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.json +7 -2
- package/dist/styles/tokens.css +73 -1
- package/dist/tokens/index.d.ts +6 -0
- 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 +29 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/kundli-render.d.ts +63 -0
- package/dist/utils/kundli-render.d.ts.map +1 -0
- package/dist/utils/string.d.ts +14 -0
- package/dist/utils/string.d.ts.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +7 -1
- package/src/components/ashtakavarga-grid.ts +354 -0
- package/src/components/biorhythm-chart.ts +39 -84
- package/src/components/choghadiya-grid.ts +185 -0
- package/src/components/compatibility-card.ts +85 -52
- package/src/components/dasha-timeline.ts +55 -73
- package/src/components/data.ts +28 -16
- package/src/components/divisional-chart.ts +214 -0
- package/src/components/dosha-card.ts +72 -68
- package/src/components/endpoint-form.ts +80 -18
- package/src/components/guna-milan.ts +87 -47
- package/src/components/hexagram.ts +53 -43
- package/src/components/horoscope-card.ts +59 -43
- package/src/components/kp-planets-table.ts +8 -27
- package/src/components/location-search.ts +47 -23
- package/src/components/moon-phase.ts +28 -25
- package/src/components/natal-chart.ts +364 -110
- package/src/components/numerology-card.ts +86 -84
- package/src/components/panchang-table.ts +40 -78
- package/src/components/shadbala-table.ts +286 -0
- package/src/components/synastry-chart.ts +213 -97
- package/src/components/tarot-card.ts +76 -62
- package/src/components/tarot-spread.ts +72 -45
- package/src/components/transits-table.ts +350 -0
- package/src/components/vedic-kundli.ts +59 -173
- package/src/components/yoga-list.ts +328 -0
- package/src/index.ts +18 -26
- package/src/manifest.ts +340 -0
- package/src/styles/tokens.css +73 -1
- package/src/tokens/index.ts +14 -0
- package/src/types/types.gen.ts +3 -3
- package/src/utils/debounce.ts +23 -4
- package/src/utils/format.ts +75 -0
- package/src/utils/kundli-render.ts +197 -0
- package/src/utils/string.ts +23 -0
- package/src/version.ts +2 -0
- package/dist/utils/motion.d.ts +0 -13
- package/dist/utils/motion.d.ts.map +0 -1
- package/src/utils/motion.ts +0 -18
|
@@ -1,41 +1,31 @@
|
|
|
1
1
|
import { css, html, LitElement, nothing, svg } from 'lit';
|
|
2
2
|
import { customElement, property } from 'lit/decorators.js';
|
|
3
|
-
import { PLANET_GLYPH, SIGN_GLYPH } from '../tokens/index.js';
|
|
3
|
+
import { PLANET_GLYPH, SIGN_GLYPH, SIGNS_ORDER } 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 {
|
|
11
|
+
ASPECT_CLASS,
|
|
12
|
+
formatNumber,
|
|
13
|
+
normalizeAspect,
|
|
14
|
+
} from '../utils/format.js';
|
|
15
|
+
import { capitalize } from '../utils/string.js';
|
|
6
16
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
planet?: string;
|
|
10
|
-
longitude?: number;
|
|
11
|
-
degree?: number;
|
|
12
|
-
sign?: string;
|
|
13
|
-
}
|
|
17
|
+
type PlanetEntry = NatalChartResponse['planets'][number];
|
|
18
|
+
type InterAspect = CalculateSynastryResponse['interAspects'][number];
|
|
14
19
|
|
|
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
|
-
}
|
|
20
|
+
// Drawing the dual wheel requires per-person planet longitudes alongside
|
|
21
|
+
// the synastry response. Callers can merge planet arrays from
|
|
22
|
+
// /astrology/natal-chart into `person1.planets` and `person2.planets`
|
|
23
|
+
// before passing the payload in; without them, the component falls back
|
|
24
|
+
// to the inter-aspects table and a status note instead of an empty wheel.
|
|
25
|
+
type SynastryWithPlanets = CalculateSynastryResponse & {
|
|
26
|
+
person1?: { planets?: PlanetEntry[] };
|
|
27
|
+
person2?: { planets?: PlanetEntry[] };
|
|
28
|
+
};
|
|
39
29
|
|
|
40
30
|
const SIZE = 360;
|
|
41
31
|
const CENTER = SIZE / 2;
|
|
@@ -104,6 +94,42 @@ export class RoxySynastryChart extends LitElement {
|
|
|
104
94
|
font-weight: 600;
|
|
105
95
|
font-size: 13px;
|
|
106
96
|
}
|
|
97
|
+
.aspect {
|
|
98
|
+
stroke-width: 0.8;
|
|
99
|
+
fill: none;
|
|
100
|
+
opacity: 0.5;
|
|
101
|
+
}
|
|
102
|
+
.aspect-trine,
|
|
103
|
+
.aspect-sextile {
|
|
104
|
+
stroke: var(--roxy-success, #16a34a);
|
|
105
|
+
}
|
|
106
|
+
.aspect-square,
|
|
107
|
+
.aspect-opposition {
|
|
108
|
+
stroke: var(--roxy-danger, #dc2626);
|
|
109
|
+
}
|
|
110
|
+
.aspect-conjunction {
|
|
111
|
+
stroke: var(--roxy-accent-fg, #b45309);
|
|
112
|
+
}
|
|
113
|
+
.aspect-other {
|
|
114
|
+
stroke: var(--roxy-muted, #71717a);
|
|
115
|
+
opacity: 0.35;
|
|
116
|
+
}
|
|
117
|
+
.legend-row {
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-wrap: wrap;
|
|
120
|
+
gap: var(--roxy-space-md, 1rem);
|
|
121
|
+
font-size: var(--roxy-text-xs, 0.75rem);
|
|
122
|
+
color: var(--roxy-muted, #71717a);
|
|
123
|
+
margin-top: calc(var(--roxy-space-xs, 0.25rem) * -1);
|
|
124
|
+
}
|
|
125
|
+
.legend-row .swatch {
|
|
126
|
+
display: inline-block;
|
|
127
|
+
width: 8px;
|
|
128
|
+
height: 8px;
|
|
129
|
+
border-radius: 50%;
|
|
130
|
+
margin-right: 4px;
|
|
131
|
+
vertical-align: middle;
|
|
132
|
+
}
|
|
107
133
|
|
|
108
134
|
.summary {
|
|
109
135
|
margin: 0;
|
|
@@ -151,24 +177,101 @@ export class RoxySynastryChart extends LitElement {
|
|
|
151
177
|
padding-left: var(--roxy-space-md, 1rem);
|
|
152
178
|
font-size: var(--roxy-text-sm, 0.875rem);
|
|
153
179
|
}
|
|
180
|
+
|
|
181
|
+
.missing-planets {
|
|
182
|
+
background: color-mix(in srgb, var(--roxy-accent, #f59e0b) 8%, transparent);
|
|
183
|
+
border: 1px solid var(--roxy-border, #e4e4e7);
|
|
184
|
+
border-radius: var(--roxy-radius-md, 8px);
|
|
185
|
+
padding: var(--roxy-space-md, 1rem);
|
|
186
|
+
color: var(--roxy-fg, #0a0a0a);
|
|
187
|
+
font-size: var(--roxy-text-sm, 0.875rem);
|
|
188
|
+
line-height: 1.5;
|
|
189
|
+
}
|
|
190
|
+
.missing-planets code {
|
|
191
|
+
font-family: var(--roxy-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
192
|
+
font-size: 0.95em;
|
|
193
|
+
background: color-mix(in srgb, var(--roxy-fg, #0a0a0a) 6%, transparent);
|
|
194
|
+
padding: 0 4px;
|
|
195
|
+
border-radius: 4px;
|
|
196
|
+
}
|
|
154
197
|
`,
|
|
155
198
|
];
|
|
156
199
|
|
|
157
200
|
@property({ attribute: false })
|
|
158
|
-
data:
|
|
201
|
+
data: SynastryWithPlanets | null = null;
|
|
159
202
|
|
|
160
203
|
render() {
|
|
161
204
|
if (!this.data)
|
|
162
205
|
return html`<div class="roxy-empty" role="status">No synastry data</div>`;
|
|
163
|
-
const {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
206
|
+
const { person1, person2, compatibilityScore, analysis } = this.data;
|
|
207
|
+
const interAspects = this.data.interAspects ?? [];
|
|
208
|
+
const p1Planets = person1?.planets ?? [];
|
|
209
|
+
const p2Planets = person2?.planets ?? [];
|
|
210
|
+
|
|
211
|
+
const score =
|
|
212
|
+
typeof compatibilityScore === 'number'
|
|
213
|
+
? Math.round(compatibilityScore)
|
|
214
|
+
: undefined;
|
|
215
|
+
const summaryText = analysis?.overall;
|
|
216
|
+
const strengths = analysis?.strengths ?? [];
|
|
217
|
+
const challenges = analysis?.challenges ?? [];
|
|
218
|
+
|
|
219
|
+
// /astrology/synastry does not return per-person planet positions, so the
|
|
220
|
+
// dual-wheel cannot be drawn from a bare synastry response. Surface this
|
|
221
|
+
// explicitly instead of rendering a blank wheel; keep the inter-aspects
|
|
222
|
+
// table when it is present so callers still get useful output.
|
|
223
|
+
const hasPlanets = p1Planets.length > 0 && p2Planets.length > 0;
|
|
224
|
+
if (!hasPlanets) {
|
|
225
|
+
return html`<div
|
|
226
|
+
class="wrap"
|
|
227
|
+
aria-label="Synastry compatibility chart"
|
|
228
|
+
>
|
|
229
|
+
<div class="head">
|
|
230
|
+
<h2 class="title">Synastry</h2>
|
|
231
|
+
${
|
|
232
|
+
typeof score === 'number'
|
|
233
|
+
? html`<span class="score" aria-label=${`Score ${score} of 100`}
|
|
234
|
+
>${score} / 100</span
|
|
235
|
+
>`
|
|
236
|
+
: nothing
|
|
237
|
+
}
|
|
238
|
+
</div>
|
|
239
|
+
<div class="missing-planets" role="status">
|
|
240
|
+
Synastry response missing planet positions. Pass
|
|
241
|
+
<code>data</code> with <code>person1.planets</code> and
|
|
242
|
+
<code>person2.planets</code> arrays from the natal-chart endpoint, or
|
|
243
|
+
use the <code><roxy-data></code> fallback.
|
|
244
|
+
</div>
|
|
245
|
+
${summaryText ? html`<p class="summary">${summaryText}</p>` : nothing}
|
|
246
|
+
${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing}
|
|
247
|
+
${
|
|
248
|
+
strengths.length > 0 || challenges.length > 0
|
|
249
|
+
? html`<div class="lists">
|
|
250
|
+
${
|
|
251
|
+
strengths.length
|
|
252
|
+
? html`<div>
|
|
253
|
+
<h3>Strengths</h3>
|
|
254
|
+
<ul>
|
|
255
|
+
${strengths.map((s) => html`<li>${s}</li>`)}
|
|
256
|
+
</ul>
|
|
257
|
+
</div>`
|
|
258
|
+
: nothing
|
|
259
|
+
}
|
|
260
|
+
${
|
|
261
|
+
challenges.length
|
|
262
|
+
? html`<div>
|
|
263
|
+
<h3>Challenges</h3>
|
|
264
|
+
<ul>
|
|
265
|
+
${challenges.map((s) => html`<li>${s}</li>`)}
|
|
266
|
+
</ul>
|
|
267
|
+
</div>`
|
|
268
|
+
: nothing
|
|
269
|
+
}
|
|
270
|
+
</div>`
|
|
271
|
+
: nothing
|
|
272
|
+
}
|
|
273
|
+
</div>`;
|
|
274
|
+
}
|
|
172
275
|
|
|
173
276
|
return html`<div
|
|
174
277
|
class="wrap"
|
|
@@ -177,9 +280,9 @@ export class RoxySynastryChart extends LitElement {
|
|
|
177
280
|
<div class="head">
|
|
178
281
|
<h2 class="title">Synastry</h2>
|
|
179
282
|
${
|
|
180
|
-
typeof
|
|
181
|
-
? html`<span class="score" aria-label=${`Score ${
|
|
182
|
-
>${
|
|
283
|
+
typeof score === 'number'
|
|
284
|
+
? html`<span class="score" aria-label=${`Score ${score} of 100`}
|
|
285
|
+
>${score} / 100</span
|
|
183
286
|
>`
|
|
184
287
|
: nothing
|
|
185
288
|
}
|
|
@@ -212,30 +315,36 @@ export class RoxySynastryChart extends LitElement {
|
|
|
212
315
|
stroke-width="0.6"
|
|
213
316
|
/>
|
|
214
317
|
${this.renderSpokes()} ${this.renderSigns()}
|
|
318
|
+
${this.renderInterAspectLines(p1Planets, p2Planets, interAspects)}
|
|
215
319
|
${this.renderRing(p1Planets, P1_R, 'p1')} ${this.renderRing(p2Planets, P2_R, 'p2')}
|
|
216
320
|
</svg>
|
|
217
|
-
|
|
321
|
+
<div class="legend-row">
|
|
322
|
+
<span><span class="swatch" style="background: var(--roxy-accent)"></span>Person 1</span>
|
|
323
|
+
<span><span class="swatch" style="background: var(--roxy-info)"></span>Person 2</span>
|
|
324
|
+
<span><span class="swatch" style="background: var(--roxy-success)"></span>harmonious</span>
|
|
325
|
+
<span><span class="swatch" style="background: var(--roxy-danger)"></span>challenging</span>
|
|
326
|
+
</div>
|
|
327
|
+
${summaryText ? html`<p class="summary">${summaryText}</p>` : nothing}
|
|
218
328
|
${interAspects.length > 0 ? this.renderAspects(interAspects) : nothing}
|
|
219
329
|
${
|
|
220
|
-
|
|
221
|
-
(this.data.challenges?.length ?? 0) > 0
|
|
330
|
+
strengths.length > 0 || challenges.length > 0
|
|
222
331
|
? html`<div class="lists">
|
|
223
332
|
${
|
|
224
|
-
|
|
333
|
+
strengths.length
|
|
225
334
|
? html`<div>
|
|
226
335
|
<h3>Strengths</h3>
|
|
227
336
|
<ul>
|
|
228
|
-
${
|
|
337
|
+
${strengths.map((s) => html`<li>${s}</li>`)}
|
|
229
338
|
</ul>
|
|
230
339
|
</div>`
|
|
231
340
|
: nothing
|
|
232
341
|
}
|
|
233
342
|
${
|
|
234
|
-
|
|
343
|
+
challenges.length
|
|
235
344
|
? html`<div>
|
|
236
345
|
<h3>Challenges</h3>
|
|
237
346
|
<ul>
|
|
238
|
-
${
|
|
347
|
+
${challenges.map((s) => html`<li>${s}</li>`)}
|
|
239
348
|
</ul>
|
|
240
349
|
</div>`
|
|
241
350
|
: nothing
|
|
@@ -246,17 +355,13 @@ export class RoxySynastryChart extends LitElement {
|
|
|
246
355
|
</div>`;
|
|
247
356
|
}
|
|
248
357
|
|
|
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 }));
|
|
358
|
+
private toAngle(longitude: number): number {
|
|
359
|
+
return 180 - longitude;
|
|
255
360
|
}
|
|
256
361
|
|
|
257
362
|
private renderSpokes() {
|
|
258
363
|
return Array.from({ length: 12 }, (_, i) => {
|
|
259
|
-
const angle = i * 30
|
|
364
|
+
const angle = this.toAngle(i * 30);
|
|
260
365
|
const start = polarToCartesian(CENTER, CENTER, P2_R - 14, angle);
|
|
261
366
|
const end = polarToCartesian(CENTER, CENTER, OUTER_R, angle);
|
|
262
367
|
return svg`<line class="wheel-line" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width="0.6" />`;
|
|
@@ -264,22 +369,8 @@ export class RoxySynastryChart extends LitElement {
|
|
|
264
369
|
}
|
|
265
370
|
|
|
266
371
|
private renderSigns() {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
'Taurus',
|
|
270
|
-
'Gemini',
|
|
271
|
-
'Cancer',
|
|
272
|
-
'Leo',
|
|
273
|
-
'Virgo',
|
|
274
|
-
'Libra',
|
|
275
|
-
'Scorpio',
|
|
276
|
-
'Sagittarius',
|
|
277
|
-
'Capricorn',
|
|
278
|
-
'Aquarius',
|
|
279
|
-
'Pisces',
|
|
280
|
-
];
|
|
281
|
-
return order.map((s, i) => {
|
|
282
|
-
const angle = i * 30 + 15 - 90;
|
|
372
|
+
return SIGNS_ORDER.map((s, i) => {
|
|
373
|
+
const angle = this.toAngle(i * 30 + 15);
|
|
283
374
|
const pos = polarToCartesian(CENTER, CENTER, SIGN_R, angle);
|
|
284
375
|
return svg`<text class="sign" x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central">${SIGN_GLYPH[s]}</text>`;
|
|
285
376
|
});
|
|
@@ -287,17 +378,44 @@ export class RoxySynastryChart extends LitElement {
|
|
|
287
378
|
|
|
288
379
|
private renderRing(planets: PlanetEntry[], radius: number, cls: string) {
|
|
289
380
|
return planets.map((p) => {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
381
|
+
if (!Number.isFinite(p.longitude)) return nothing;
|
|
382
|
+
const pos = polarToCartesian(
|
|
383
|
+
CENTER,
|
|
384
|
+
CENTER,
|
|
385
|
+
radius,
|
|
386
|
+
this.toAngle(p.longitude),
|
|
387
|
+
);
|
|
388
|
+
const glyph = PLANET_GLYPH[capitalize(p.name)] ?? p.name.slice(0, 2);
|
|
389
|
+
return svg`<text class=${cls} x=${pos.x} y=${pos.y} text-anchor="middle" dominant-baseline="central"><title>${p.name}</title>${glyph}</text>`;
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
private renderInterAspectLines(
|
|
394
|
+
p1: PlanetEntry[],
|
|
395
|
+
p2: PlanetEntry[],
|
|
396
|
+
aspects: InterAspect[],
|
|
397
|
+
) {
|
|
398
|
+
const longitudeOf = (
|
|
399
|
+
list: PlanetEntry[],
|
|
400
|
+
name: string,
|
|
401
|
+
): number | undefined => {
|
|
402
|
+
const target = capitalize(name);
|
|
403
|
+
for (const p of list) {
|
|
404
|
+
if (capitalize(p.name) !== target) continue;
|
|
405
|
+
if (typeof p.longitude === 'number') return p.longitude;
|
|
406
|
+
}
|
|
407
|
+
return undefined;
|
|
408
|
+
};
|
|
409
|
+
return aspects.map((a) => {
|
|
410
|
+
const l1 = longitudeOf(p1, a.planet1);
|
|
411
|
+
const l2 = longitudeOf(p2, a.planet2);
|
|
412
|
+
if (l1 === undefined || l2 === undefined) return nothing;
|
|
413
|
+
const out = polarToCartesian(CENTER, CENTER, P1_R - 12, this.toAngle(l1));
|
|
414
|
+
const inn = polarToCartesian(CENTER, CENTER, P2_R + 8, this.toAngle(l2));
|
|
415
|
+
const aspectName = normalizeAspect(a);
|
|
416
|
+
const cls = ASPECT_CLASS[aspectName] ?? 'aspect-other';
|
|
417
|
+
const orbLabel = formatNumber(a.orb, 1);
|
|
418
|
+
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
419
|
});
|
|
302
420
|
}
|
|
303
421
|
|
|
@@ -313,15 +431,13 @@ export class RoxySynastryChart extends LitElement {
|
|
|
313
431
|
</tr>
|
|
314
432
|
</thead>
|
|
315
433
|
<tbody>
|
|
316
|
-
${aspects.slice(0,
|
|
434
|
+
${aspects.slice(0, 12).map(
|
|
317
435
|
(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>
|
|
436
|
+
<td>${a.planet1}</td>
|
|
437
|
+
<td>${a.planet2}</td>
|
|
438
|
+
<td>${normalizeAspect(a) || ''}</td>
|
|
439
|
+
<td class="orb">${formatNumber(a.orb, 1)}</td>
|
|
440
|
+
<td>${formatStrength(a.strength)}</td>
|
|
325
441
|
</tr>`,
|
|
326
442
|
)}
|
|
327
443
|
</tbody>
|
|
@@ -329,9 +445,9 @@ export class RoxySynastryChart extends LitElement {
|
|
|
329
445
|
}
|
|
330
446
|
}
|
|
331
447
|
|
|
332
|
-
function
|
|
333
|
-
if (
|
|
334
|
-
return
|
|
448
|
+
function formatStrength(s: number | undefined): string {
|
|
449
|
+
if (typeof s === 'number') return Math.round(s).toString();
|
|
450
|
+
return '';
|
|
335
451
|
}
|
|
336
452
|
|
|
337
453
|
declare global {
|
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
import { css, html, LitElement, nothing } from 'lit';
|
|
2
2
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
3
|
+
import type { GetCardResponse, GetDailyCardResponse } from '../types/index.js';
|
|
3
4
|
import { baseStyles } from '../utils/base-styles.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
id?: string;
|
|
7
|
-
name?: string;
|
|
8
|
-
arcana?: 'major' | 'minor' | string;
|
|
9
|
-
number?: number | string;
|
|
10
|
-
position?: string;
|
|
11
|
-
reversed?: boolean;
|
|
12
|
-
keywords?: string[];
|
|
13
|
-
meaning?:
|
|
14
|
-
| string
|
|
15
|
-
| {
|
|
16
|
-
upright?: string;
|
|
17
|
-
reversed?: string;
|
|
18
|
-
spiritual?: string;
|
|
19
|
-
emotional?: string;
|
|
20
|
-
physical?: string;
|
|
21
|
-
};
|
|
22
|
-
imageUrl?: string;
|
|
23
|
-
upright?: { meaning?: string; keywords?: string[] };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface TarotData {
|
|
27
|
-
date?: string;
|
|
28
|
-
seed?: string;
|
|
29
|
-
card?: TarotCard;
|
|
30
|
-
dailyMessage?: string;
|
|
31
|
-
}
|
|
6
|
+
type TarotData = GetCardResponse | GetDailyCardResponse;
|
|
32
7
|
|
|
33
8
|
/**
|
|
34
9
|
* Tarot card. Renders /tarot/cards/{id} or /tarot/daily. Click to flip
|
|
@@ -92,11 +67,6 @@ export class RoxyTarotCard extends LitElement {
|
|
|
92
67
|
letter-spacing: 0.06em;
|
|
93
68
|
margin-bottom: var(--roxy-space-sm, 0.5rem);
|
|
94
69
|
}
|
|
95
|
-
.position {
|
|
96
|
-
color: var(--roxy-info, #0284c7);
|
|
97
|
-
margin-left: var(--roxy-space-xs, 0.25rem);
|
|
98
|
-
text-transform: capitalize;
|
|
99
|
-
}
|
|
100
70
|
|
|
101
71
|
.message {
|
|
102
72
|
color: var(--roxy-fg, #0a0a0a);
|
|
@@ -137,37 +107,28 @@ export class RoxyTarotCard extends LitElement {
|
|
|
137
107
|
];
|
|
138
108
|
|
|
139
109
|
@property({ attribute: false })
|
|
140
|
-
data: TarotData |
|
|
110
|
+
data: TarotData | null = null;
|
|
141
111
|
|
|
142
112
|
@state()
|
|
143
113
|
private flipped = false;
|
|
144
114
|
|
|
145
|
-
private getCard(): TarotCard | null {
|
|
146
|
-
if (!this.data) return null;
|
|
147
|
-
if ('card' in this.data && this.data.card) return this.data.card;
|
|
148
|
-
return this.data as TarotCard;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
115
|
private toggleFlip = () => {
|
|
152
116
|
this.flipped = !this.flipped;
|
|
153
117
|
};
|
|
154
118
|
|
|
155
119
|
render() {
|
|
156
|
-
const
|
|
157
|
-
if (!
|
|
120
|
+
const d = this.data;
|
|
121
|
+
if (!d)
|
|
158
122
|
return html`<div class="roxy-empty" role="status">No tarot data</div>`;
|
|
159
123
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
this.data && 'dailyMessage' in this.data
|
|
169
|
-
? this.data.dailyMessage
|
|
170
|
-
: undefined;
|
|
124
|
+
if ('card' in d) return this.renderDailyCard(d);
|
|
125
|
+
return this.renderFullCard(d);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private renderDailyCard(d: GetDailyCardResponse) {
|
|
129
|
+
const card = d.card;
|
|
130
|
+
const isReversed = this.flipped !== Boolean(card.reversed);
|
|
131
|
+
const keywords = card.keywords ?? [];
|
|
171
132
|
|
|
172
133
|
return html`<article class="card" aria-label=${card.name ?? 'Tarot card'}>
|
|
173
134
|
<div class="image-wrap">
|
|
@@ -197,21 +158,74 @@ export class RoxyTarotCard extends LitElement {
|
|
|
197
158
|
<div>
|
|
198
159
|
<div class="meta">
|
|
199
160
|
${card.arcana ? html`${card.arcana} arcana` : nothing}
|
|
200
|
-
${card.number !== undefined && card.number !== null ? html` · ${card.number}` : nothing}
|
|
201
161
|
${isReversed ? html` · reversed` : nothing}
|
|
202
|
-
${
|
|
203
|
-
card.position
|
|
204
|
-
? html`<span class="position">${card.position}</span>`
|
|
205
|
-
: nothing
|
|
206
|
-
}
|
|
207
162
|
</div>
|
|
208
163
|
<h2 class="title">${card.name ?? 'Tarot card'}</h2>
|
|
209
|
-
${dailyMessage ? html`<p class="message">${dailyMessage}</p>` : nothing}
|
|
210
|
-
${meaning ? html`<p>${meaning}</p>` : nothing}
|
|
164
|
+
${d.dailyMessage ? html`<p class="message">${d.dailyMessage}</p>` : nothing}
|
|
165
|
+
${card.meaning ? html`<p>${card.meaning}</p>` : nothing}
|
|
166
|
+
${
|
|
167
|
+
keywords.length > 0
|
|
168
|
+
? html`<div class="chips">
|
|
169
|
+
${keywords.map((k) => html`<span>${k}</span>`)}
|
|
170
|
+
</div>`
|
|
171
|
+
: nothing
|
|
172
|
+
}
|
|
173
|
+
<button
|
|
174
|
+
class="flip"
|
|
175
|
+
type="button"
|
|
176
|
+
@click=${this.toggleFlip}
|
|
177
|
+
aria-pressed=${this.flipped ? 'true' : 'false'}
|
|
178
|
+
>
|
|
179
|
+
Flip card
|
|
180
|
+
</button>
|
|
181
|
+
</div>
|
|
182
|
+
</article>`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private renderFullCard(d: GetCardResponse) {
|
|
186
|
+
const isReversed = this.flipped;
|
|
187
|
+
const orientedMeaning = isReversed ? d.reversed : d.upright;
|
|
188
|
+
const keywords = isReversed
|
|
189
|
+
? (d.keywords?.reversed ?? [])
|
|
190
|
+
: (d.keywords?.upright ?? []);
|
|
191
|
+
|
|
192
|
+
return html`<article class="card" aria-label=${d.name ?? 'Tarot card'}>
|
|
193
|
+
<div class="image-wrap">
|
|
194
|
+
${
|
|
195
|
+
d.imageUrl
|
|
196
|
+
? html`<img
|
|
197
|
+
class=${`image ${isReversed ? 'reversed' : ''}`}
|
|
198
|
+
src=${d.imageUrl}
|
|
199
|
+
alt=${d.name ?? 'Tarot card'}
|
|
200
|
+
tabindex="0"
|
|
201
|
+
@click=${this.toggleFlip}
|
|
202
|
+
@keydown=${(e: KeyboardEvent) => {
|
|
203
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
204
|
+
e.preventDefault();
|
|
205
|
+
this.toggleFlip();
|
|
206
|
+
}
|
|
207
|
+
}}
|
|
208
|
+
/>`
|
|
209
|
+
: html`<div
|
|
210
|
+
class=${`image ${isReversed ? 'reversed' : ''}`}
|
|
211
|
+
style="aspect-ratio: 0.6; display: flex; align-items: center; justify-content: center; color: var(--roxy-muted)"
|
|
212
|
+
>
|
|
213
|
+
${d.name ?? '?'}
|
|
214
|
+
</div>`
|
|
215
|
+
}
|
|
216
|
+
</div>
|
|
217
|
+
<div>
|
|
218
|
+
<div class="meta">
|
|
219
|
+
${d.arcana ? html`${d.arcana} arcana` : nothing}
|
|
220
|
+
${d.number !== undefined && d.number !== null ? html` · ${d.number}` : nothing}
|
|
221
|
+
${isReversed ? html` · reversed` : nothing}
|
|
222
|
+
</div>
|
|
223
|
+
<h2 class="title">${d.name ?? 'Tarot card'}</h2>
|
|
224
|
+
${orientedMeaning?.description ? html`<p>${orientedMeaning.description}</p>` : nothing}
|
|
211
225
|
${
|
|
212
|
-
|
|
226
|
+
keywords.length > 0
|
|
213
227
|
? html`<div class="chips">
|
|
214
|
-
${
|
|
228
|
+
${keywords.map((k) => html`<span>${k}</span>`)}
|
|
215
229
|
</div>`
|
|
216
230
|
: nothing
|
|
217
231
|
}
|