@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.
Files changed (169) hide show
  1. package/AGENTS.md +2 -2
  2. package/LICENSE +21 -0
  3. package/README.md +505 -0
  4. package/THEMING.md +24 -7
  5. package/dist/cdn/components/biorhythm-chart.js +15 -22
  6. package/dist/cdn/components/biorhythm-chart.js.map +3 -3
  7. package/dist/cdn/components/compatibility-card.js +36 -34
  8. package/dist/cdn/components/compatibility-card.js.map +4 -4
  9. package/dist/cdn/components/dasha-timeline.js +35 -39
  10. package/dist/cdn/components/dasha-timeline.js.map +4 -4
  11. package/dist/cdn/components/data.js +6 -6
  12. package/dist/cdn/components/data.js.map +3 -3
  13. package/dist/cdn/components/dosha-card.js +13 -13
  14. package/dist/cdn/components/dosha-card.js.map +2 -2
  15. package/dist/cdn/components/endpoint-form.js +47 -28
  16. package/dist/cdn/components/endpoint-form.js.map +3 -3
  17. package/dist/cdn/components/guna-milan.js +18 -18
  18. package/dist/cdn/components/guna-milan.js.map +4 -4
  19. package/dist/cdn/components/hexagram.js +26 -26
  20. package/dist/cdn/components/hexagram.js.map +3 -3
  21. package/dist/cdn/components/horoscope-card.js +38 -38
  22. package/dist/cdn/components/horoscope-card.js.map +3 -3
  23. package/dist/cdn/components/kp-planets-table.js +10 -10
  24. package/dist/cdn/components/kp-planets-table.js.map +4 -4
  25. package/dist/cdn/components/location-search.js +6 -6
  26. package/dist/cdn/components/location-search.js.map +3 -3
  27. package/dist/cdn/components/moon-phase.js +21 -21
  28. package/dist/cdn/components/moon-phase.js.map +4 -4
  29. package/dist/cdn/components/natal-chart.js +61 -19
  30. package/dist/cdn/components/natal-chart.js.map +4 -4
  31. package/dist/cdn/components/numerology-card.js +40 -31
  32. package/dist/cdn/components/numerology-card.js.map +3 -3
  33. package/dist/cdn/components/panchang-table.js +25 -25
  34. package/dist/cdn/components/panchang-table.js.map +4 -4
  35. package/dist/cdn/components/synastry-chart.js +129 -39
  36. package/dist/cdn/components/synastry-chart.js.map +4 -4
  37. package/dist/cdn/components/tarot-card.js +49 -20
  38. package/dist/cdn/components/tarot-card.js.map +3 -3
  39. package/dist/cdn/components/tarot-spread.js +43 -27
  40. package/dist/cdn/components/tarot-spread.js.map +3 -3
  41. package/dist/cdn/components/vedic-kundli.js +23 -9
  42. package/dist/cdn/components/vedic-kundli.js.map +3 -3
  43. package/dist/cdn/roxy-ui.js +560 -350
  44. package/dist/cdn/roxy-ui.js.map +4 -4
  45. package/dist/components/biorhythm-chart.d.ts +2 -46
  46. package/dist/components/biorhythm-chart.d.ts.map +1 -1
  47. package/dist/components/biorhythm-chart.js +24 -23
  48. package/dist/components/biorhythm-chart.js.map +2 -2
  49. package/dist/components/compatibility-card.d.ts +2 -27
  50. package/dist/components/compatibility-card.d.ts.map +1 -1
  51. package/dist/components/compatibility-card.js +50 -29
  52. package/dist/components/compatibility-card.js.map +3 -3
  53. package/dist/components/dasha-timeline.d.ts +2 -31
  54. package/dist/components/dasha-timeline.d.ts.map +1 -1
  55. package/dist/components/dasha-timeline.js +32 -30
  56. package/dist/components/dasha-timeline.js.map +3 -3
  57. package/dist/components/data.d.ts +6 -0
  58. package/dist/components/data.d.ts.map +1 -1
  59. package/dist/components/data.js +9 -1
  60. package/dist/components/data.js.map +2 -2
  61. package/dist/components/dosha-card.d.ts +2 -16
  62. package/dist/components/dosha-card.d.ts.map +1 -1
  63. package/dist/components/dosha-card.js +12 -13
  64. package/dist/components/dosha-card.js.map +2 -2
  65. package/dist/components/endpoint-form.d.ts +2 -0
  66. package/dist/components/endpoint-form.d.ts.map +1 -1
  67. package/dist/components/endpoint-form.js +66 -8
  68. package/dist/components/endpoint-form.js.map +2 -2
  69. package/dist/components/guna-milan.d.ts +2 -20
  70. package/dist/components/guna-milan.d.ts.map +1 -1
  71. package/dist/components/guna-milan.js +22 -12
  72. package/dist/components/guna-milan.js.map +3 -3
  73. package/dist/components/hexagram.d.ts +3 -27
  74. package/dist/components/hexagram.d.ts.map +1 -1
  75. package/dist/components/hexagram.js +31 -15
  76. package/dist/components/hexagram.js.map +2 -2
  77. package/dist/components/horoscope-card.d.ts +2 -20
  78. package/dist/components/horoscope-card.d.ts.map +1 -1
  79. package/dist/components/horoscope-card.js +24 -15
  80. package/dist/components/horoscope-card.js.map +2 -2
  81. package/dist/components/kp-planets-table.d.ts +2 -21
  82. package/dist/components/kp-planets-table.d.ts.map +1 -1
  83. package/dist/components/kp-planets-table.js +10 -4
  84. package/dist/components/kp-planets-table.js.map +3 -3
  85. package/dist/components/location-search.d.ts +3 -11
  86. package/dist/components/location-search.d.ts.map +1 -1
  87. package/dist/components/location-search.js +45 -5
  88. package/dist/components/location-search.js.map +2 -2
  89. package/dist/components/moon-phase.d.ts +4 -21
  90. package/dist/components/moon-phase.d.ts.map +1 -1
  91. package/dist/components/moon-phase.js +17 -4
  92. package/dist/components/moon-phase.js.map +3 -3
  93. package/dist/components/natal-chart.d.ts +7 -43
  94. package/dist/components/natal-chart.d.ts.map +1 -1
  95. package/dist/components/natal-chart.js +130 -70
  96. package/dist/components/natal-chart.js.map +3 -3
  97. package/dist/components/numerology-card.d.ts +5 -37
  98. package/dist/components/numerology-card.d.ts.map +1 -1
  99. package/dist/components/numerology-card.js +54 -28
  100. package/dist/components/numerology-card.js.map +2 -2
  101. package/dist/components/panchang-table.d.ts +3 -62
  102. package/dist/components/panchang-table.d.ts.map +1 -1
  103. package/dist/components/panchang-table.js +62 -32
  104. package/dist/components/panchang-table.js.map +3 -3
  105. package/dist/components/synastry-chart.d.ts +9 -28
  106. package/dist/components/synastry-chart.d.ts.map +1 -1
  107. package/dist/components/synastry-chart.js +178 -38
  108. package/dist/components/synastry-chart.js.map +3 -3
  109. package/dist/components/tarot-card.d.ts +5 -29
  110. package/dist/components/tarot-card.d.ts.map +1 -1
  111. package/dist/components/tarot-card.js +59 -20
  112. package/dist/components/tarot-card.js.map +2 -2
  113. package/dist/components/tarot-spread.d.ts +2 -24
  114. package/dist/components/tarot-spread.d.ts.map +1 -1
  115. package/dist/components/tarot-spread.js +39 -13
  116. package/dist/components/tarot-spread.js.map +2 -2
  117. package/dist/components/vedic-kundli.d.ts +3 -23
  118. package/dist/components/vedic-kundli.d.ts.map +1 -1
  119. package/dist/components/vedic-kundli.js +25 -13
  120. package/dist/components/vedic-kundli.js.map +2 -2
  121. package/dist/index.cjs +1149 -358
  122. package/dist/index.cjs.map +4 -4
  123. package/dist/index.d.ts +6 -4
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +1149 -358
  126. package/dist/index.js.map +4 -4
  127. package/dist/manifest.d.ts +49 -0
  128. package/dist/manifest.d.ts.map +1 -0
  129. package/dist/manifest.json +1 -1
  130. package/dist/styles/tokens.css +47 -1
  131. package/dist/tokens/index.d.ts.map +1 -1
  132. package/dist/types/index.d.ts +2 -0
  133. package/dist/types/index.d.ts.map +1 -0
  134. package/dist/types/types.gen.d.ts +27811 -0
  135. package/dist/types/types.gen.d.ts.map +1 -0
  136. package/dist/utils/debounce.d.ts +9 -1
  137. package/dist/utils/debounce.d.ts.map +1 -1
  138. package/dist/utils/format.d.ts +15 -0
  139. package/dist/utils/format.d.ts.map +1 -0
  140. package/dist/version.d.ts +2 -0
  141. package/dist/version.d.ts.map +1 -0
  142. package/package.json +9 -1
  143. package/src/components/biorhythm-chart.ts +39 -84
  144. package/src/components/compatibility-card.ts +85 -52
  145. package/src/components/dasha-timeline.ts +55 -73
  146. package/src/components/data.ts +20 -1
  147. package/src/components/dosha-card.ts +18 -31
  148. package/src/components/endpoint-form.ts +79 -11
  149. package/src/components/guna-milan.ts +16 -34
  150. package/src/components/hexagram.ts +53 -43
  151. package/src/components/horoscope-card.ts +51 -39
  152. package/src/components/kp-planets-table.ts +8 -27
  153. package/src/components/location-search.ts +45 -20
  154. package/src/components/moon-phase.ts +28 -25
  155. package/src/components/natal-chart.ts +129 -84
  156. package/src/components/numerology-card.ts +87 -79
  157. package/src/components/panchang-table.ts +40 -78
  158. package/src/components/synastry-chart.ts +220 -78
  159. package/src/components/tarot-card.ts +76 -62
  160. package/src/components/tarot-spread.ts +72 -45
  161. package/src/components/vedic-kundli.ts +42 -51
  162. package/src/index.ts +14 -24
  163. package/src/manifest.ts +366 -0
  164. package/src/styles/tokens.css +47 -1
  165. package/src/tokens/index.ts +5 -0
  166. package/src/types/types.gen.ts +1 -1
  167. package/src/utils/debounce.ts +23 -4
  168. package/src/utils/format.ts +57 -0
  169. package/src/version.ts +2 -0
@@ -100,6 +100,21 @@ var baseStyles = css`
100
100
  `;
101
101
 
102
102
  // packages/ui/src/components/endpoint-form.ts
103
+ var specCache = /* @__PURE__ */ new Map();
104
+ async function loadSpec(url) {
105
+ let pending = specCache.get(url);
106
+ if (!pending) {
107
+ pending = fetch(url).then(async (res) => {
108
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
109
+ return await res.json();
110
+ }).catch((err) => {
111
+ specCache.delete(url);
112
+ throw err;
113
+ });
114
+ specCache.set(url, pending);
115
+ }
116
+ return pending;
117
+ }
103
118
  var RoxyEndpointForm = class extends LitElement {
104
119
  constructor() {
105
120
  super(...arguments);
@@ -111,6 +126,12 @@ var RoxyEndpointForm = class extends LitElement {
111
126
  this.values = {};
112
127
  this.hasLocation = false;
113
128
  this.loaded = false;
129
+ this.specError = null;
130
+ this.retryLoadSchema = () => {
131
+ this.loaded = false;
132
+ this.specError = null;
133
+ void this.loadSchema();
134
+ };
114
135
  this.onLocation = (e) => {
115
136
  const detail = e.detail;
116
137
  if (detail) {
@@ -151,13 +172,16 @@ var RoxyEndpointForm = class extends LitElement {
151
172
  void this.loadSchema();
152
173
  }
153
174
  async loadSchema() {
175
+ this.specError = null;
154
176
  try {
155
- const res = await fetch(this.specUrl);
156
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
157
- const spec = await res.json();
177
+ const spec = await loadSpec(this.specUrl);
158
178
  const path = `/${this.endpoint.replace(/^\//, "")}`;
159
179
  const op = spec.paths?.[path]?.[this.method.toLowerCase()];
160
- if (!op) return;
180
+ if (!op) {
181
+ throw new Error(
182
+ `Endpoint ${this.method} ${path} not found in OpenAPI spec`
183
+ );
184
+ }
161
185
  const schemas = spec.components?.schemas ?? {};
162
186
  const fields = [];
163
187
  let bodySchema;
@@ -202,8 +226,17 @@ var RoxyEndpointForm = class extends LitElement {
202
226
  }
203
227
  this.values = init;
204
228
  this.loaded = true;
205
- } catch (_err) {
229
+ } catch (err) {
230
+ const message = err instanceof Error ? err.message : String(err);
231
+ this.specError = message;
206
232
  this.loaded = true;
233
+ this.dispatchEvent(
234
+ new CustomEvent("roxy-spec-error", {
235
+ detail: { url: this.specUrl, message },
236
+ bubbles: true,
237
+ composed: true
238
+ })
239
+ );
207
240
  }
208
241
  }
209
242
  resolve(schema, all) {
@@ -229,6 +262,12 @@ var RoxyEndpointForm = class extends LitElement {
229
262
  if (!this.loaded) {
230
263
  return html`<form><div class="roxy-skeleton" style="height: 8rem"></div></form>`;
231
264
  }
265
+ if (this.specError) {
266
+ return html`<div class="spec-error" role="alert">
267
+ Schema load failed: ${this.specError}
268
+ <button type="button" class="submit" @click=${this.retryLoadSchema}>Retry</button>
269
+ </div>`;
270
+ }
232
271
  const renderField = (f) => {
233
272
  if (this.hasLocation && (f.name === "latitude" || f.name === "longitude" || f.name === "timezone")) {
234
273
  return nothing;
@@ -326,22 +365,27 @@ RoxyEndpointForm.styles = [
326
365
  .fields {
327
366
  display: grid;
328
367
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
368
+ align-items: start;
329
369
  gap: var(--roxy-space-md, 1rem);
330
370
  }
331
371
  .field {
332
- display: grid;
372
+ display: flex;
373
+ flex-direction: column;
333
374
  gap: var(--roxy-space-xs, 0.25rem);
375
+ min-width: 0;
334
376
  }
335
377
  label {
336
378
  font-size: var(--roxy-text-sm, 0.875rem);
337
379
  color: var(--roxy-secondary, #475569);
338
380
  }
339
381
  label .req {
340
- color: var(--roxy-danger, #dc2626);
382
+ color: var(--roxy-danger-fg, #991b1b);
341
383
  margin-left: 4px;
342
384
  }
343
385
  input,
344
386
  select {
387
+ width: 100%;
388
+ box-sizing: border-box;
345
389
  padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);
346
390
  font-size: var(--roxy-text-base, 1rem);
347
391
  font-family: inherit;
@@ -376,7 +420,7 @@ RoxyEndpointForm.styles = [
376
420
  button.submit {
377
421
  justify-self: start;
378
422
  background: var(--roxy-accent-fg, #b45309);
379
- color: #fff;
423
+ color: var(--roxy-bg, #fff);
380
424
  border: 0;
381
425
  border-radius: var(--roxy-radius-md, 8px);
382
426
  padding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-lg, 1.5rem);
@@ -394,6 +438,17 @@ RoxyEndpointForm.styles = [
394
438
  outline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));
395
439
  outline-offset: 2px;
396
440
  }
441
+ .spec-error {
442
+ display: grid;
443
+ gap: var(--roxy-space-md, 1rem);
444
+ justify-items: start;
445
+ background: var(--roxy-bg, #fff);
446
+ border: 1px solid var(--roxy-danger, #dc2626);
447
+ border-radius: var(--roxy-radius-md, 8px);
448
+ padding: var(--roxy-space-lg, 1.5rem);
449
+ color: var(--roxy-danger-fg, #991b1b);
450
+ font-size: var(--roxy-text-sm, 0.875rem);
451
+ }
397
452
  `
398
453
  ];
399
454
  __decorateClass([
@@ -420,6 +475,9 @@ __decorateClass([
420
475
  __decorateClass([
421
476
  state()
422
477
  ], RoxyEndpointForm.prototype, "loaded", 2);
478
+ __decorateClass([
479
+ state()
480
+ ], RoxyEndpointForm.prototype, "specError", 2);
423
481
  RoxyEndpointForm = __decorateClass([
424
482
  customElement("roxy-endpoint-form")
425
483
  ], RoxyEndpointForm);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/endpoint-form.ts", "../../src/utils/base-styles.ts"],
4
- "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { baseStyles } from '../utils/base-styles.js';\n\ninterface OpenApiSchemaRef {\n\t$ref?: string;\n}\n\ninterface OpenApiSchema extends OpenApiSchemaRef {\n\ttype?: string;\n\tformat?: string;\n\tdescription?: string;\n\tenum?: string[];\n\tdefault?: unknown;\n\tminimum?: number;\n\tmaximum?: number;\n\tproperties?: Record<string, OpenApiSchema>;\n\trequired?: string[];\n\titems?: OpenApiSchema;\n\texample?: unknown;\n}\n\ninterface FieldDef {\n\tname: string;\n\ttype: string;\n\trequired: boolean;\n\tdescription?: string;\n\tenum?: string[];\n\tmin?: number;\n\tmax?: number;\n\tdefault?: unknown;\n}\n\n/**\n * Schema-driven form. Pass `endpoint` (e.g. \"vedic-astrology/birth-chart\").\n * The form introspects the cached OpenAPI spec, slots a roxy-location-search\n * when latitude+longitude+timezone fields are present, and emits a\n * `roxy-submit` CustomEvent with the validated payload on submit. The caller\n * decides what to do (call the SDK, render a chart, navigate).\n *\n * Build-time hints (x-roxy-ui formGroups) are read by scripts/build.ts and\n * baked into a static map. At runtime the component falls back to runtime\n * fetch of /api/v2/openapi.json when no map is provided.\n */\n@customElement('roxy-endpoint-form')\nexport class RoxyEndpointForm extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\tform {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\t\t\tbox-shadow: var(--roxy-shadow-sm);\n\t\t\t}\n\t\t\t.title {\n\t\t\t\tmargin: 0;\n\t\t\t\tfont-size: var(--roxy-text-lg, 1.125rem);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\t\t\t.fields {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgrid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\t\t\t.field {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-xs, 0.25rem);\n\t\t\t}\n\t\t\tlabel {\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-secondary, #475569);\n\t\t\t}\n\t\t\tlabel .req {\n\t\t\t\tcolor: var(--roxy-danger, #dc2626);\n\t\t\t\tmargin-left: 4px;\n\t\t\t}\n\t\t\tinput,\n\t\t\tselect {\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t\tfont-family: inherit;\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t}\n\t\t\tinput:focus,\n\t\t\tselect:focus {\n\t\t\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\t\t\toutline-offset: 2px;\n\t\t\t\tborder-color: var(--roxy-accent-fg, #b45309);\n\t\t\t}\n\t\t\t.help {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t}\n\t\t\t.location-block {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-xs, 0.25rem);\n\t\t\t\tgrid-column: 1 / -1;\n\t\t\t}\n\t\t\t.coords {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgrid-template-columns: repeat(3, 1fr);\n\t\t\t\tgap: var(--roxy-space-sm, 0.5rem);\n\t\t\t}\n\t\t\t.coords input {\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t}\n\t\t\tbutton.submit {\n\t\t\t\tjustify-self: start;\n\t\t\t\tbackground: var(--roxy-accent-fg, #b45309);\n\t\t\t\tcolor: #fff;\n\t\t\t\tborder: 0;\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-lg, 1.5rem);\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\tcursor: pointer;\n\t\t\t\ttransition:\n\t\t\t\t\ttransform var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t}\n\t\t\tbutton.submit:hover {\n\t\t\t\ttransform: scale(1.02);\n\t\t\t}\n\t\t\tbutton.submit:focus-visible {\n\t\t\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\t\t\toutline-offset: 2px;\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ type: String, attribute: 'data-endpoint' })\n\tendpoint = 'vedic-astrology/birth-chart';\n\n\t@property({ type: String })\n\tmethod: 'GET' | 'POST' = 'POST';\n\n\t@property({ type: String, attribute: 'spec-url' })\n\tspecUrl = 'https://roxyapi.com/api/v2/openapi.json';\n\n\t@property({ type: String, attribute: 'submit-label' })\n\tsubmitLabel = 'Submit';\n\n\t@state()\n\tprivate fields: FieldDef[] = [];\n\n\t@state()\n\tprivate values: Record<string, unknown> = {};\n\n\t@state()\n\tprivate hasLocation = false;\n\n\t@state()\n\tprivate loaded = false;\n\n\tconnectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tvoid this.loadSchema();\n\t}\n\n\tprivate async loadSchema() {\n\t\ttry {\n\t\t\tconst res = await fetch(this.specUrl);\n\t\t\tif (!res.ok) throw new Error(`HTTP ${res.status}`);\n\t\t\tconst spec = (await res.json()) as {\n\t\t\t\tpaths?: Record<string, Record<string, unknown>>;\n\t\t\t\tcomponents?: { schemas?: Record<string, OpenApiSchema> };\n\t\t\t};\n\t\t\tconst path = `/${this.endpoint.replace(/^\\//, '')}`;\n\t\t\tconst op = spec.paths?.[path]?.[this.method.toLowerCase()] as\n\t\t\t\t| {\n\t\t\t\t\t\trequestBody?: {\n\t\t\t\t\t\t\tcontent?: Record<\n\t\t\t\t\t\t\t\tstring,\n\t\t\t\t\t\t\t\t{ schema?: OpenApiSchema | OpenApiSchemaRef }\n\t\t\t\t\t\t\t>;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tparameters?: Array<{\n\t\t\t\t\t\t\tname: string;\n\t\t\t\t\t\t\tin: string;\n\t\t\t\t\t\t\trequired?: boolean;\n\t\t\t\t\t\t\tschema?: OpenApiSchema;\n\t\t\t\t\t\t}>;\n\t\t\t\t }\n\t\t\t\t| undefined;\n\t\t\tif (!op) return;\n\n\t\t\tconst schemas = spec.components?.schemas ?? {};\n\t\t\tconst fields: FieldDef[] = [];\n\t\t\tlet bodySchema: OpenApiSchema | undefined;\n\n\t\t\tif (op.requestBody) {\n\t\t\t\tconst ref = op.requestBody.content?.['application/json']?.schema;\n\t\t\t\tbodySchema = this.resolve(ref, schemas);\n\t\t\t}\n\n\t\t\tif (bodySchema?.properties) {\n\t\t\t\tconst required = new Set(bodySchema.required ?? []);\n\t\t\t\tfor (const [name, sub] of Object.entries(bodySchema.properties)) {\n\t\t\t\t\tconst resolved = this.resolve(sub, schemas) ?? {};\n\t\t\t\t\tfields.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\ttype: this.fieldType(resolved),\n\t\t\t\t\t\trequired: required.has(name),\n\t\t\t\t\t\tdescription: resolved.description,\n\t\t\t\t\t\tenum: resolved.enum,\n\t\t\t\t\t\tmin: resolved.minimum,\n\t\t\t\t\t\tmax: resolved.maximum,\n\t\t\t\t\t\tdefault: resolved.default,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const param of op.parameters ?? []) {\n\t\t\t\tif (param.in === 'path' || param.in === 'query') {\n\t\t\t\t\tconst resolved = this.resolve(param.schema, schemas) ?? {};\n\t\t\t\t\tfields.push({\n\t\t\t\t\t\tname: param.name,\n\t\t\t\t\t\ttype: this.fieldType(resolved),\n\t\t\t\t\t\trequired: !!param.required,\n\t\t\t\t\t\tdescription: resolved.description,\n\t\t\t\t\t\tenum: resolved.enum,\n\t\t\t\t\t\tdefault: resolved.default,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.fields = fields;\n\t\t\tthis.hasLocation =\n\t\t\t\tfields.some((f) => f.name === 'latitude') &&\n\t\t\t\tfields.some((f) => f.name === 'longitude') &&\n\t\t\t\tfields.some((f) => f.name === 'timezone');\n\n\t\t\t// Pre-fill defaults\n\t\t\tconst init: Record<string, unknown> = {};\n\t\t\tfor (const f of fields) {\n\t\t\t\tif (f.default !== undefined) init[f.name] = f.default;\n\t\t\t}\n\t\t\tthis.values = init;\n\t\t\tthis.loaded = true;\n\t\t} catch (_err) {\n\t\t\tthis.loaded = true;\n\t\t}\n\t}\n\n\tprivate resolve(\n\t\tschema: OpenApiSchema | OpenApiSchemaRef | undefined,\n\t\tall: Record<string, OpenApiSchema>,\n\t): OpenApiSchema | undefined {\n\t\tif (!schema) return undefined;\n\t\tif ('$ref' in schema && schema.$ref) {\n\t\t\tconst name = schema.$ref.split('/').pop();\n\t\t\treturn name ? all[name] : undefined;\n\t\t}\n\t\treturn schema as OpenApiSchema;\n\t}\n\n\tprivate fieldType(s: OpenApiSchema): string {\n\t\tif (s.enum) return 'enum';\n\t\tif (s.format === 'date') return 'date';\n\t\tif (s.format === 'time') return 'time';\n\t\tif (s.format === 'date-time') return 'datetime';\n\t\tif (s.type === 'integer' || s.type === 'number') return 'number';\n\t\treturn 'text';\n\t}\n\n\tprivate setValue(name: string, value: unknown) {\n\t\tthis.values = { ...this.values, [name]: value };\n\t}\n\n\tprivate onLocation = (e: Event) => {\n\t\tconst detail = (e as CustomEvent).detail as {\n\t\t\tlatitude?: number;\n\t\t\tlongitude?: number;\n\t\t\ttimezone?: string;\n\t\t\tutcOffset?: number;\n\t\t};\n\t\tif (detail) {\n\t\t\tthis.values = {\n\t\t\t\t...this.values,\n\t\t\t\tlatitude: detail.latitude,\n\t\t\t\tlongitude: detail.longitude,\n\t\t\t\ttimezone: detail.timezone ?? detail.utcOffset,\n\t\t\t};\n\t\t}\n\t};\n\n\tprivate onSubmit = (e: Event) => {\n\t\te.preventDefault();\n\t\tconst missing = this.fields\n\t\t\t.filter((f) => f.required)\n\t\t\t.filter(\n\t\t\t\t(f) => this.values[f.name] === undefined || this.values[f.name] === '',\n\t\t\t);\n\t\tif (missing.length > 0) {\n\t\t\tthis.dispatchEvent(\n\t\t\t\tnew CustomEvent('roxy-validation-error', {\n\t\t\t\t\tdetail: { missing: missing.map((m) => m.name) },\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcomposed: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent('roxy-submit', {\n\t\t\t\tdetail: { endpoint: this.endpoint, values: this.values },\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t}),\n\t\t);\n\t};\n\n\trender() {\n\t\tif (!this.loaded) {\n\t\t\treturn html`<form><div class=\"roxy-skeleton\" style=\"height: 8rem\"></div></form>`;\n\t\t}\n\n\t\tconst renderField = (f: FieldDef) => {\n\t\t\tif (\n\t\t\t\tthis.hasLocation &&\n\t\t\t\t(f.name === 'latitude' ||\n\t\t\t\t\tf.name === 'longitude' ||\n\t\t\t\t\tf.name === 'timezone')\n\t\t\t) {\n\t\t\t\treturn nothing;\n\t\t\t}\n\t\t\tconst inputId = `roxy-form-${f.name}`;\n\t\t\treturn html`<div class=\"field\">\n\t\t\t\t<label for=${inputId}>\n\t\t\t\t\t${humanize(f.name)}${f.required ? html`<span class=\"req\" aria-hidden=\"true\">*</span>` : nothing}\n\t\t\t\t</label>\n\t\t\t\t${\n\t\t\t\t\tf.enum\n\t\t\t\t\t\t? html`<select\n\t\t\t\t\t\t\tid=${inputId}\n\t\t\t\t\t\t\t?required=${f.required}\n\t\t\t\t\t\t\t@change=${(e: Event) => this.setValue(f.name, (e.target as HTMLSelectElement).value)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"\">Choose</option>\n\t\t\t\t\t\t\t${f.enum.map(\n\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\topt,\n\t\t\t\t\t\t\t\t) => html`<option value=${opt} ?selected=${this.values[f.name] === opt}>\n\t\t\t\t\t\t\t\t\t${opt}\n\t\t\t\t\t\t\t\t</option>`,\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</select>`\n\t\t\t\t\t\t: html`<input\n\t\t\t\t\t\t\tid=${inputId}\n\t\t\t\t\t\t\ttype=${this.htmlType(f.type)}\n\t\t\t\t\t\t\t?required=${f.required}\n\t\t\t\t\t\t\tmin=${f.min ?? ''}\n\t\t\t\t\t\t\tmax=${f.max ?? ''}\n\t\t\t\t\t\t\tstep=${f.type === 'number' ? 'any' : ''}\n\t\t\t\t\t\t\t.value=${(this.values[f.name] ?? '') as string}\n\t\t\t\t\t\t\t@input=${(e: Event) =>\n\t\t\t\t\t\t\t\tthis.setValue(\n\t\t\t\t\t\t\t\t\tf.name,\n\t\t\t\t\t\t\t\t\tthis.coerce(f.type, (e.target as HTMLInputElement).value),\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>`\n\t\t\t\t}\n\t\t\t\t${f.description ? html`<small class=\"help\">${f.description}</small>` : nothing}\n\t\t\t</div>`;\n\t\t};\n\n\t\treturn html`<form @submit=${this.onSubmit}>\n\t\t\t<h2 class=\"title\">${humanize(this.endpoint.split('/').pop() ?? '')}</h2>\n\t\t\t${\n\t\t\t\tthis.hasLocation\n\t\t\t\t\t? html`<div class=\"location-block\">\n\t\t\t\t\t\t<label>Birth location</label>\n\t\t\t\t\t\t<roxy-location-search\n\t\t\t\t\t\t\t@roxy-location-select=${this.onLocation}\n\t\t\t\t\t\t\tplaceholder=\"City of birth\"\n\t\t\t\t\t\t></roxy-location-search>\n\t\t\t\t\t\t<small class=\"help\">\n\t\t\t\t\t\t\tRequired: latitude, longitude, timezone. Pick a city to autofill.\n\t\t\t\t\t\t</small>\n\t\t\t\t\t</div>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t<div class=\"fields\">\n\t\t\t\t${this.fields.map((f) => renderField(f))}\n\t\t\t</div>\n\t\t\t<button class=\"submit\" type=\"submit\">${this.submitLabel}</button>\n\t\t</form>`;\n\t}\n\n\tprivate htmlType(t: string): string {\n\t\tswitch (t) {\n\t\t\tcase 'date':\n\t\t\t\treturn 'date';\n\t\t\tcase 'time':\n\t\t\t\treturn 'time';\n\t\t\tcase 'datetime':\n\t\t\t\treturn 'datetime-local';\n\t\t\tcase 'number':\n\t\t\t\treturn 'number';\n\t\t\tdefault:\n\t\t\t\treturn 'text';\n\t\t}\n\t}\n\n\tprivate coerce(t: string, v: string): unknown {\n\t\tif (v === '') return undefined;\n\t\tif (t === 'number') {\n\t\t\tconst n = Number(v);\n\t\t\treturn Number.isFinite(n) ? n : undefined;\n\t\t}\n\t\treturn v;\n\t}\n}\n\nfunction humanize(s: string): string {\n\treturn s\n\t\t.replace(/[_-]+/g, ' ')\n\t\t.replace(/([a-z])([A-Z])/g, '$1 $2')\n\t\t.replace(/^\\w/, (c) => c.toUpperCase());\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-endpoint-form': RoxyEndpointForm;\n\t}\n}\n", "import { css } from 'lit';\n\n/**\n * Shared host styles every component pulls in. Sets default font, color,\n * container query support, and the entry fade-in.\n */\nexport const baseStyles = css`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\n\t}\n`;\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { baseStyles } from '../utils/base-styles.js';\n\ninterface OpenApiSchemaRef {\n\t$ref?: string;\n}\n\ninterface OpenApiSchema extends OpenApiSchemaRef {\n\ttype?: string;\n\tformat?: string;\n\tdescription?: string;\n\tenum?: string[];\n\tdefault?: unknown;\n\tminimum?: number;\n\tmaximum?: number;\n\tproperties?: Record<string, OpenApiSchema>;\n\trequired?: string[];\n\titems?: OpenApiSchema;\n\texample?: unknown;\n}\n\ninterface FieldDef {\n\tname: string;\n\ttype: string;\n\trequired: boolean;\n\tdescription?: string;\n\tenum?: string[];\n\tmin?: number;\n\tmax?: number;\n\tdefault?: unknown;\n}\n\ninterface OpenApiDoc {\n\tpaths?: Record<string, Record<string, unknown>>;\n\tcomponents?: { schemas?: Record<string, OpenApiSchema> };\n}\n\nconst specCache = new Map<string, Promise<OpenApiDoc>>();\n\nasync function loadSpec(url: string): Promise<OpenApiDoc> {\n\tlet pending = specCache.get(url);\n\tif (!pending) {\n\t\tpending = fetch(url)\n\t\t\t.then(async (res) => {\n\t\t\t\tif (!res.ok) throw new Error(`HTTP ${res.status}`);\n\t\t\t\treturn (await res.json()) as OpenApiDoc;\n\t\t\t})\n\t\t\t.catch((err) => {\n\t\t\t\t// Evict the rejected promise BEFORE rethrowing so subsequent\n\t\t\t\t// callers (the user clicking Retry, a remount) hit the network\n\t\t\t\t// again instead of replaying the cached failure forever.\n\t\t\t\tspecCache.delete(url);\n\t\t\t\tthrow err;\n\t\t\t});\n\t\tspecCache.set(url, pending);\n\t}\n\treturn pending;\n}\n\n/**\n * Schema-driven form. Pass `endpoint` (e.g. \"vedic-astrology/birth-chart\").\n * The form introspects the cached OpenAPI spec, slots a roxy-location-search\n * when latitude+longitude+timezone fields are present, and emits a\n * `roxy-submit` CustomEvent with the validated payload on submit. The caller\n * decides what to do (call the SDK, render a chart, navigate).\n *\n * Build-time hints (x-roxy-ui formGroups) are read by scripts/build.ts and\n * baked into a static map. At runtime the component falls back to runtime\n * fetch of /api/v2/openapi.json when no map is provided.\n */\n@customElement('roxy-endpoint-form')\nexport class RoxyEndpointForm extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\tform {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\t\t\tbox-shadow: var(--roxy-shadow-sm);\n\t\t\t}\n\t\t\t.title {\n\t\t\t\tmargin: 0;\n\t\t\t\tfont-size: var(--roxy-text-lg, 1.125rem);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\t\t\t.fields {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgrid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));\n\t\t\t\talign-items: start;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\t\t\t.field {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\tgap: var(--roxy-space-xs, 0.25rem);\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\t\t\tlabel {\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-secondary, #475569);\n\t\t\t}\n\t\t\tlabel .req {\n\t\t\t\tcolor: var(--roxy-danger-fg, #991b1b);\n\t\t\t\tmargin-left: 4px;\n\t\t\t}\n\t\t\tinput,\n\t\t\tselect {\n\t\t\t\twidth: 100%;\n\t\t\t\tbox-sizing: border-box;\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-md, 1rem);\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t\tfont-family: inherit;\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t}\n\t\t\tinput:focus,\n\t\t\tselect:focus {\n\t\t\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\t\t\toutline-offset: 2px;\n\t\t\t\tborder-color: var(--roxy-accent-fg, #b45309);\n\t\t\t}\n\t\t\t.help {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t}\n\t\t\t.location-block {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-xs, 0.25rem);\n\t\t\t\tgrid-column: 1 / -1;\n\t\t\t}\n\t\t\t.coords {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgrid-template-columns: repeat(3, 1fr);\n\t\t\t\tgap: var(--roxy-space-sm, 0.5rem);\n\t\t\t}\n\t\t\t.coords input {\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t}\n\t\t\tbutton.submit {\n\t\t\t\tjustify-self: start;\n\t\t\t\tbackground: var(--roxy-accent-fg, #b45309);\n\t\t\t\tcolor: var(--roxy-bg, #fff);\n\t\t\t\tborder: 0;\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem) var(--roxy-space-lg, 1.5rem);\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\tcursor: pointer;\n\t\t\t\ttransition:\n\t\t\t\t\ttransform var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t}\n\t\t\tbutton.submit:hover {\n\t\t\t\ttransform: scale(1.02);\n\t\t\t}\n\t\t\tbutton.submit:focus-visible {\n\t\t\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\t\t\toutline-offset: 2px;\n\t\t\t}\n\t\t\t.spec-error {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t\tjustify-items: start;\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-danger, #dc2626);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\t\t\tcolor: var(--roxy-danger-fg, #991b1b);\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ type: String, attribute: 'data-endpoint' })\n\tendpoint = 'vedic-astrology/birth-chart';\n\n\t@property({ type: String })\n\tmethod: 'GET' | 'POST' = 'POST';\n\n\t@property({ type: String, attribute: 'spec-url' })\n\tspecUrl = 'https://roxyapi.com/api/v2/openapi.json';\n\n\t@property({ type: String, attribute: 'submit-label' })\n\tsubmitLabel = 'Submit';\n\n\t@state()\n\tprivate fields: FieldDef[] = [];\n\n\t@state()\n\tprivate values: Record<string, unknown> = {};\n\n\t@state()\n\tprivate hasLocation = false;\n\n\t@state()\n\tprivate loaded = false;\n\n\t@state()\n\tprivate specError: string | null = null;\n\n\tconnectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tvoid this.loadSchema();\n\t}\n\n\tprivate async loadSchema() {\n\t\tthis.specError = null;\n\t\ttry {\n\t\t\tconst spec = await loadSpec(this.specUrl);\n\t\t\tconst path = `/${this.endpoint.replace(/^\\//, '')}`;\n\t\t\tconst op = spec.paths?.[path]?.[this.method.toLowerCase()] as\n\t\t\t\t| {\n\t\t\t\t\t\trequestBody?: {\n\t\t\t\t\t\t\tcontent?: Record<\n\t\t\t\t\t\t\t\tstring,\n\t\t\t\t\t\t\t\t{ schema?: OpenApiSchema | OpenApiSchemaRef }\n\t\t\t\t\t\t\t>;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tparameters?: Array<{\n\t\t\t\t\t\t\tname: string;\n\t\t\t\t\t\t\tin: string;\n\t\t\t\t\t\t\trequired?: boolean;\n\t\t\t\t\t\t\tschema?: OpenApiSchema;\n\t\t\t\t\t\t}>;\n\t\t\t\t }\n\t\t\t\t| undefined;\n\t\t\tif (!op) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Endpoint ${this.method} ${path} not found in OpenAPI spec`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst schemas = spec.components?.schemas ?? {};\n\t\t\tconst fields: FieldDef[] = [];\n\t\t\tlet bodySchema: OpenApiSchema | undefined;\n\n\t\t\tif (op.requestBody) {\n\t\t\t\tconst ref = op.requestBody.content?.['application/json']?.schema;\n\t\t\t\tbodySchema = this.resolve(ref, schemas);\n\t\t\t}\n\n\t\t\tif (bodySchema?.properties) {\n\t\t\t\tconst required = new Set(bodySchema.required ?? []);\n\t\t\t\tfor (const [name, sub] of Object.entries(bodySchema.properties)) {\n\t\t\t\t\tconst resolved = this.resolve(sub, schemas) ?? {};\n\t\t\t\t\tfields.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\ttype: this.fieldType(resolved),\n\t\t\t\t\t\trequired: required.has(name),\n\t\t\t\t\t\tdescription: resolved.description,\n\t\t\t\t\t\tenum: resolved.enum,\n\t\t\t\t\t\tmin: resolved.minimum,\n\t\t\t\t\t\tmax: resolved.maximum,\n\t\t\t\t\t\tdefault: resolved.default,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const param of op.parameters ?? []) {\n\t\t\t\tif (param.in === 'path' || param.in === 'query') {\n\t\t\t\t\tconst resolved = this.resolve(param.schema, schemas) ?? {};\n\t\t\t\t\tfields.push({\n\t\t\t\t\t\tname: param.name,\n\t\t\t\t\t\ttype: this.fieldType(resolved),\n\t\t\t\t\t\trequired: !!param.required,\n\t\t\t\t\t\tdescription: resolved.description,\n\t\t\t\t\t\tenum: resolved.enum,\n\t\t\t\t\t\tdefault: resolved.default,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.fields = fields;\n\t\t\tthis.hasLocation =\n\t\t\t\tfields.some((f) => f.name === 'latitude') &&\n\t\t\t\tfields.some((f) => f.name === 'longitude') &&\n\t\t\t\tfields.some((f) => f.name === 'timezone');\n\n\t\t\t// Pre-fill defaults\n\t\t\tconst init: Record<string, unknown> = {};\n\t\t\tfor (const f of fields) {\n\t\t\t\tif (f.default !== undefined) init[f.name] = f.default;\n\t\t\t}\n\t\t\tthis.values = init;\n\t\t\tthis.loaded = true;\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.specError = message;\n\t\t\tthis.loaded = true;\n\t\t\tthis.dispatchEvent(\n\t\t\t\tnew CustomEvent('roxy-spec-error', {\n\t\t\t\t\tdetail: { url: this.specUrl, message },\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcomposed: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate retryLoadSchema = () => {\n\t\tthis.loaded = false;\n\t\tthis.specError = null;\n\t\tvoid this.loadSchema();\n\t};\n\n\tprivate resolve(\n\t\tschema: OpenApiSchema | OpenApiSchemaRef | undefined,\n\t\tall: Record<string, OpenApiSchema>,\n\t): OpenApiSchema | undefined {\n\t\tif (!schema) return undefined;\n\t\tif ('$ref' in schema && schema.$ref) {\n\t\t\tconst name = schema.$ref.split('/').pop();\n\t\t\treturn name ? all[name] : undefined;\n\t\t}\n\t\treturn schema as OpenApiSchema;\n\t}\n\n\tprivate fieldType(s: OpenApiSchema): string {\n\t\tif (s.enum) return 'enum';\n\t\tif (s.format === 'date') return 'date';\n\t\tif (s.format === 'time') return 'time';\n\t\tif (s.format === 'date-time') return 'datetime';\n\t\tif (s.type === 'integer' || s.type === 'number') return 'number';\n\t\treturn 'text';\n\t}\n\n\tprivate setValue(name: string, value: unknown) {\n\t\tthis.values = { ...this.values, [name]: value };\n\t}\n\n\tprivate onLocation = (e: Event) => {\n\t\tconst detail = (e as CustomEvent).detail as {\n\t\t\tlatitude?: number;\n\t\t\tlongitude?: number;\n\t\t\ttimezone?: string;\n\t\t\tutcOffset?: number;\n\t\t};\n\t\tif (detail) {\n\t\t\tthis.values = {\n\t\t\t\t...this.values,\n\t\t\t\tlatitude: detail.latitude,\n\t\t\t\tlongitude: detail.longitude,\n\t\t\t\ttimezone: detail.timezone ?? detail.utcOffset,\n\t\t\t};\n\t\t}\n\t};\n\n\tprivate onSubmit = (e: Event) => {\n\t\te.preventDefault();\n\t\tconst missing = this.fields\n\t\t\t.filter((f) => f.required)\n\t\t\t.filter(\n\t\t\t\t(f) => this.values[f.name] === undefined || this.values[f.name] === '',\n\t\t\t);\n\t\tif (missing.length > 0) {\n\t\t\tthis.dispatchEvent(\n\t\t\t\tnew CustomEvent('roxy-validation-error', {\n\t\t\t\t\tdetail: { missing: missing.map((m) => m.name) },\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcomposed: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent('roxy-submit', {\n\t\t\t\tdetail: { endpoint: this.endpoint, values: this.values },\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t}),\n\t\t);\n\t};\n\n\trender() {\n\t\tif (!this.loaded) {\n\t\t\treturn html`<form><div class=\"roxy-skeleton\" style=\"height: 8rem\"></div></form>`;\n\t\t}\n\n\t\tif (this.specError) {\n\t\t\treturn html`<div class=\"spec-error\" role=\"alert\">\n\t\t\t\tSchema load failed: ${this.specError}\n\t\t\t\t<button type=\"button\" class=\"submit\" @click=${this.retryLoadSchema}>Retry</button>\n\t\t\t</div>`;\n\t\t}\n\n\t\tconst renderField = (f: FieldDef) => {\n\t\t\tif (\n\t\t\t\tthis.hasLocation &&\n\t\t\t\t(f.name === 'latitude' ||\n\t\t\t\t\tf.name === 'longitude' ||\n\t\t\t\t\tf.name === 'timezone')\n\t\t\t) {\n\t\t\t\treturn nothing;\n\t\t\t}\n\t\t\tconst inputId = `roxy-form-${f.name}`;\n\t\t\treturn html`<div class=\"field\">\n\t\t\t\t<label for=${inputId}>\n\t\t\t\t\t${humanize(f.name)}${f.required ? html`<span class=\"req\" aria-hidden=\"true\">*</span>` : nothing}\n\t\t\t\t</label>\n\t\t\t\t${\n\t\t\t\t\tf.enum\n\t\t\t\t\t\t? html`<select\n\t\t\t\t\t\t\tid=${inputId}\n\t\t\t\t\t\t\t?required=${f.required}\n\t\t\t\t\t\t\t@change=${(e: Event) => this.setValue(f.name, (e.target as HTMLSelectElement).value)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"\">Choose</option>\n\t\t\t\t\t\t\t${f.enum.map(\n\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\topt,\n\t\t\t\t\t\t\t\t) => html`<option value=${opt} ?selected=${this.values[f.name] === opt}>\n\t\t\t\t\t\t\t\t\t${opt}\n\t\t\t\t\t\t\t\t</option>`,\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</select>`\n\t\t\t\t\t\t: html`<input\n\t\t\t\t\t\t\tid=${inputId}\n\t\t\t\t\t\t\ttype=${this.htmlType(f.type)}\n\t\t\t\t\t\t\t?required=${f.required}\n\t\t\t\t\t\t\tmin=${f.min ?? ''}\n\t\t\t\t\t\t\tmax=${f.max ?? ''}\n\t\t\t\t\t\t\tstep=${f.type === 'number' ? 'any' : ''}\n\t\t\t\t\t\t\t.value=${(this.values[f.name] ?? '') as string}\n\t\t\t\t\t\t\t@input=${(e: Event) =>\n\t\t\t\t\t\t\t\tthis.setValue(\n\t\t\t\t\t\t\t\t\tf.name,\n\t\t\t\t\t\t\t\t\tthis.coerce(f.type, (e.target as HTMLInputElement).value),\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>`\n\t\t\t\t}\n\t\t\t\t${f.description ? html`<small class=\"help\">${f.description}</small>` : nothing}\n\t\t\t</div>`;\n\t\t};\n\n\t\treturn html`<form @submit=${this.onSubmit}>\n\t\t\t<h2 class=\"title\">${humanize(this.endpoint.split('/').pop() ?? '')}</h2>\n\t\t\t${\n\t\t\t\tthis.hasLocation\n\t\t\t\t\t? html`<div class=\"location-block\">\n\t\t\t\t\t\t<label>Birth location</label>\n\t\t\t\t\t\t<roxy-location-search\n\t\t\t\t\t\t\t@roxy-location-select=${this.onLocation}\n\t\t\t\t\t\t\tplaceholder=\"City of birth\"\n\t\t\t\t\t\t></roxy-location-search>\n\t\t\t\t\t\t<small class=\"help\">\n\t\t\t\t\t\t\tRequired: latitude, longitude, timezone. Pick a city to autofill.\n\t\t\t\t\t\t</small>\n\t\t\t\t\t</div>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t<div class=\"fields\">\n\t\t\t\t${this.fields.map((f) => renderField(f))}\n\t\t\t</div>\n\t\t\t<button class=\"submit\" type=\"submit\">${this.submitLabel}</button>\n\t\t</form>`;\n\t}\n\n\tprivate htmlType(t: string): string {\n\t\tswitch (t) {\n\t\t\tcase 'date':\n\t\t\t\treturn 'date';\n\t\t\tcase 'time':\n\t\t\t\treturn 'time';\n\t\t\tcase 'datetime':\n\t\t\t\treturn 'datetime-local';\n\t\t\tcase 'number':\n\t\t\t\treturn 'number';\n\t\t\tdefault:\n\t\t\t\treturn 'text';\n\t\t}\n\t}\n\n\tprivate coerce(t: string, v: string): unknown {\n\t\tif (v === '') return undefined;\n\t\tif (t === 'number') {\n\t\t\tconst n = Number(v);\n\t\t\treturn Number.isFinite(n) ? n : undefined;\n\t\t}\n\t\treturn v;\n\t}\n}\n\nfunction humanize(s: string): string {\n\treturn s\n\t\t.replace(/[_-]+/g, ' ')\n\t\t.replace(/([a-z])([A-Z])/g, '$1 $2')\n\t\t.replace(/^\\w/, (c) => c.toUpperCase());\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-endpoint-form': RoxyEndpointForm;\n\t}\n}\n", "import { css } from 'lit';\n\n/**\n * Shared host styles every component pulls in. Sets default font, color,\n * container query support, and the entry fade-in.\n */\nexport const baseStyles = css`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\n\t}\n`;\n"],
5
+ "mappings": "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",
6
6
  "names": ["css", "css"]
7
7
  }
@@ -1,28 +1,11 @@
1
1
  import { LitElement } from 'lit';
2
- interface GunaCategory {
3
- name?: string;
4
- score?: number;
5
- max?: number;
6
- maxScore?: number;
7
- description?: string;
8
- }
9
- interface GunaData {
10
- total?: number;
11
- totalScore?: number;
12
- maxScore?: number;
13
- percentage?: number;
14
- isCompatible?: boolean;
15
- recommendation?: string;
16
- doshas?: string[];
17
- doshaCancellations?: string[];
18
- breakdown?: GunaCategory[];
19
- }
2
+ import type { CompatibilityResponse } from '../types/index.js';
20
3
  /**
21
4
  * 36-point Ashtakoota score card. Renders /vedic-astrology/compatibility.
22
5
  */
23
6
  export declare class RoxyGunaMilan extends LitElement {
24
7
  static styles: import("lit").CSSResult[];
25
- data: GunaData | null;
8
+ data: CompatibilityResponse | null;
26
9
  render(): import("lit").TemplateResult<1>;
27
10
  }
28
11
  export declare const GUNA_CATEGORIES: string[];
@@ -31,5 +14,4 @@ declare global {
31
14
  'roxy-guna-milan': RoxyGunaMilan;
32
15
  }
33
16
  }
34
- export {};
35
17
  //# sourceMappingURL=guna-milan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"guna-milan.d.ts","sourceRoot":"","sources":["../../src/components/guna-milan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAIrD,UAAU,YAAY;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,QAAQ;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAaD;;GAEG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC5C,MAAM,CAAC,MAAM,4BAgGX;IAGF,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAE7B,MAAM;CAsEN;AA2BD,eAAO,MAAM,eAAe,UAAsB,CAAC;AAEnD,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,iBAAiB,EAAE,aAAa,CAAC;KACjC;CACD"}
1
+ {"version":3,"file":"guna-milan.d.ts","sourceRoot":"","sources":["../../src/components/guna-milan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAErD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAe/D;;GAEG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC5C,MAAM,CAAC,MAAM,4BAgGX;IAGF,IAAI,EAAE,qBAAqB,GAAG,IAAI,CAAQ;IAE1C,MAAM;CAuEN;AA0BD,eAAO,MAAM,eAAe,UAAsB,CAAC;AAEnD,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,iBAAiB,EAAE,aAAa,CAAC;KACjC;CACD"}
@@ -99,6 +99,16 @@ var baseStyles = css`
99
99
  }
100
100
  `;
101
101
 
102
+ // packages/ui/src/utils/format.ts
103
+ function formatNumber(value, dp = 1) {
104
+ if (typeof value !== "number" || !Number.isFinite(value)) return "";
105
+ return value.toFixed(dp).replace(/\.?0+$/, "");
106
+ }
107
+ function formatPercent(value, dp = 1) {
108
+ const n = formatNumber(value, dp);
109
+ return n ? `${n}%` : "";
110
+ }
111
+
102
112
  // packages/ui/src/components/guna-milan.ts
103
113
  var STANDARD_CATEGORIES = [
104
114
  "Varna",
@@ -119,18 +129,16 @@ var RoxyGunaMilan = class extends LitElement {
119
129
  const d = this.data;
120
130
  if (!d)
121
131
  return html`<div class="roxy-empty" role="status">No Guna Milan data</div>`;
122
- const total = d.total ?? d.totalScore ?? 0;
123
- const max = d.maxScore ?? 36;
124
132
  const breakdown = (d.breakdown ?? []).filter(
125
- (b) => b && (b.name || b.score !== void 0)
133
+ (b) => b?.category !== void 0
126
134
  );
127
135
  return html`<article class="card" aria-label="Guna Milan score">
128
136
  <div class="score-bar">
129
137
  <div>
130
- <span class="total">${total}</span>
131
- <span class="over"> / ${max}</span>
138
+ <span class="total">${formatNumber(d.total, 1)}</span>
139
+ <span class="over"> / ${d.maxScore}</span>
132
140
  ${typeof d.percentage === "number" ? html`<small style="margin-left: 0.5rem; color: var(--roxy-muted)">
133
- ${d.percentage}%
141
+ ${formatPercent(d.percentage, 1)}
134
142
  </small>` : nothing}
135
143
  </div>
136
144
  ${d.recommendation ? html`<span class="recommendation">${d.recommendation}</span>` : nothing}
@@ -147,23 +155,25 @@ var RoxyGunaMilan = class extends LitElement {
147
155
  <tbody>
148
156
  ${breakdown.map((b) => {
149
157
  const score = b.score ?? 0;
150
- const maxScore = b.max ?? b.maxScore ?? defaultMax(b.name);
158
+ const maxScore = b.maxScore ?? defaultMax(b.category);
151
159
  const pct = maxScore ? score / maxScore * 100 : 0;
152
160
  return html`<tr>
153
- <td>${b.name ?? ""}</td>
161
+ <td>${b.category}</td>
154
162
  <td class="bar-cell">
155
163
  <div class="mini-bar">
156
164
  <span style="width: ${pct}%"></span>
157
165
  </div>
158
166
  </td>
159
- <td class="score">${score} / ${maxScore}</td>
167
+ <td class="score">${formatNumber(score, 1)} / ${maxScore}</td>
160
168
  </tr>`;
161
169
  })}
162
170
  </tbody>
163
171
  </table>` : nothing}
164
172
  ${(d.doshas?.length ?? 0) > 0 || (d.doshaCancellations?.length ?? 0) > 0 ? html`<div class="tags">
165
173
  ${d.doshas?.map((x) => html`<span class="dosha">${x}</span>`)}
166
- ${d.doshaCancellations?.map((x) => html`<span class="cancel">${x}</span>`)}
174
+ ${d.doshaCancellations?.map(
175
+ (x) => html`<span class="cancel" title=${x.reason}>${x.dosha} cancelled</span>`
176
+ )}
167
177
  </div>` : nothing}
168
178
  </article>`;
169
179
  }
@@ -257,11 +267,11 @@ RoxyGunaMilan.styles = [
257
267
  }
258
268
  .tags .dosha {
259
269
  background: color-mix(in srgb, var(--roxy-danger, #dc2626) 16%, transparent);
260
- color: var(--roxy-danger, #dc2626);
270
+ color: var(--roxy-danger-fg, #991b1b);
261
271
  }
262
272
  .tags .cancel {
263
273
  background: color-mix(in srgb, var(--roxy-success, #16a34a) 18%, transparent);
264
- color: var(--roxy-success, #16a34a);
274
+ color: var(--roxy-success-fg, #166534);
265
275
  }
266
276
  `
267
277
  ];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/components/guna-milan.ts", "../../src/utils/base-styles.ts"],
4
- "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { baseStyles } from '../utils/base-styles.js';\n\ninterface GunaCategory {\n\tname?: string;\n\tscore?: number;\n\tmax?: number;\n\tmaxScore?: number;\n\tdescription?: string;\n}\n\ninterface GunaData {\n\ttotal?: number;\n\ttotalScore?: number;\n\tmaxScore?: number;\n\tpercentage?: number;\n\tisCompatible?: boolean;\n\trecommendation?: string;\n\tdoshas?: string[];\n\tdoshaCancellations?: string[];\n\tbreakdown?: GunaCategory[];\n}\n\nconst STANDARD_CATEGORIES = [\n\t'Varna',\n\t'Vasya',\n\t'Tara',\n\t'Yoni',\n\t'Maitri',\n\t'Gana',\n\t'Bhakoot',\n\t'Nadi',\n];\n\n/**\n * 36-point Ashtakoota score card. Renders /vedic-astrology/compatibility.\n */\n@customElement('roxy-guna-milan')\nexport class RoxyGunaMilan extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\t.card {\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\t\t\tbox-shadow: var(--roxy-shadow-sm);\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\n\t\t\t.score-bar {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgrid-template-columns: 1fr auto;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\t\t\t.total {\n\t\t\t\tfont-size: 2.25rem;\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\tcolor: var(--roxy-accent-fg, #b45309);\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t\tline-height: 1;\n\t\t\t}\n\t\t\t.over {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t}\n\t\t\t.recommendation {\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-secondary, #475569);\n\t\t\t}\n\n\t\t\ttable {\n\t\t\t\twidth: 100%;\n\t\t\t\tborder-collapse: collapse;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t}\n\t\t\tth,\n\t\t\ttd {\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem);\n\t\t\t\tborder-bottom: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\ttext-align: left;\n\t\t\t}\n\t\t\tth {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\ttext-transform: uppercase;\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tletter-spacing: 0.06em;\n\t\t\t}\n\t\t\ttd.score {\n\t\t\t\ttext-align: right;\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\t\t\ttd.bar-cell {\n\t\t\t\twidth: 30%;\n\t\t\t}\n\t\t\t.mini-bar {\n\t\t\t\theight: 8px;\n\t\t\t\tbackground: var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-full, 9999px);\n\t\t\t\toverflow: hidden;\n\t\t\t}\n\t\t\t.mini-bar > span {\n\t\t\t\tdisplay: block;\n\t\t\t\theight: 100%;\n\t\t\t\tbackground: var(--roxy-accent, #f59e0b);\n\t\t\t\ttransition:\n\t\t\t\t\twidth var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t}\n\n\t\t\t.tags {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: var(--roxy-space-xs, 0.25rem);\n\t\t\t}\n\t\t\t.tags span {\n\t\t\t\tpadding: 2px 8px;\n\t\t\t\tborder-radius: var(--roxy-radius-full, 9999px);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t}\n\t\t\t.tags .dosha {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-danger, #dc2626) 16%, transparent);\n\t\t\t\tcolor: var(--roxy-danger, #dc2626);\n\t\t\t}\n\t\t\t.tags .cancel {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-success, #16a34a) 18%, transparent);\n\t\t\t\tcolor: var(--roxy-success, #16a34a);\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ attribute: false })\n\tdata: GunaData | null = null;\n\n\trender() {\n\t\tconst d = this.data;\n\t\tif (!d)\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">No Guna Milan data</div>`;\n\n\t\tconst total = d.total ?? d.totalScore ?? 0;\n\t\tconst max = d.maxScore ?? 36;\n\t\tconst breakdown = (d.breakdown ?? []).filter(\n\t\t\t(b) => b && (b.name || b.score !== undefined),\n\t\t);\n\n\t\treturn html`<article class=\"card\" aria-label=\"Guna Milan score\">\n\t\t\t<div class=\"score-bar\">\n\t\t\t\t<div>\n\t\t\t\t\t<span class=\"total\">${total}</span>\n\t\t\t\t\t<span class=\"over\"> / ${max}</span>\n\t\t\t\t\t${\n\t\t\t\t\t\ttypeof d.percentage === 'number'\n\t\t\t\t\t\t\t? html`<small style=\"margin-left: 0.5rem; color: var(--roxy-muted)\">\n\t\t\t\t\t\t\t\t${d.percentage}%\n\t\t\t\t\t\t\t</small>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t${\n\t\t\t\t\td.recommendation\n\t\t\t\t\t\t? html`<span class=\"recommendation\">${d.recommendation}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t</div>\n\n\t\t\t${\n\t\t\t\tbreakdown.length > 0\n\t\t\t\t\t? html`<table>\n\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<th>Category</th>\n\t\t\t\t\t\t\t\t<th>Progress</th>\n\t\t\t\t\t\t\t\t<th class=\"score\">Score</th>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t${breakdown.map((b) => {\n\t\t\t\t\t\t\t\tconst score = b.score ?? 0;\n\t\t\t\t\t\t\t\tconst maxScore = b.max ?? b.maxScore ?? defaultMax(b.name);\n\t\t\t\t\t\t\t\tconst pct = maxScore ? (score / maxScore) * 100 : 0;\n\t\t\t\t\t\t\t\treturn html`<tr>\n\t\t\t\t\t\t\t\t\t<td>${b.name ?? ''}</td>\n\t\t\t\t\t\t\t\t\t<td class=\"bar-cell\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"mini-bar\">\n\t\t\t\t\t\t\t\t\t\t\t<span style=\"width: ${pct}%\"></span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td class=\"score\">${score} / ${maxScore}</td>\n\t\t\t\t\t\t\t\t</tr>`;\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</table>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t${\n\t\t\t\t(d.doshas?.length ?? 0) > 0 || (d.doshaCancellations?.length ?? 0) > 0\n\t\t\t\t\t? html`<div class=\"tags\">\n\t\t\t\t\t\t${d.doshas?.map((x) => html`<span class=\"dosha\">${x}</span>`)}\n\t\t\t\t\t\t${d.doshaCancellations?.map((x) => html`<span class=\"cancel\">${x}</span>`)}\n\t\t\t\t\t</div>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t</article>`;\n\t}\n}\n\nfunction defaultMax(name?: string): number {\n\tif (!name) return 1;\n\tswitch (name.toLowerCase()) {\n\t\tcase 'varna':\n\t\t\treturn 1;\n\t\tcase 'vasya':\n\t\t\treturn 2;\n\t\tcase 'tara':\n\t\t\treturn 3;\n\t\tcase 'yoni':\n\t\t\treturn 4;\n\t\tcase 'maitri':\n\t\t\treturn 5;\n\t\tcase 'gana':\n\t\t\treturn 6;\n\t\tcase 'bhakoot':\n\t\t\treturn 7;\n\t\tcase 'nadi':\n\t\t\treturn 8;\n\t\tdefault:\n\t\t\treturn 1;\n\t}\n}\n\n// Reference list (kept for documentation, used at codegen time)\nexport const GUNA_CATEGORIES = STANDARD_CATEGORIES;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-guna-milan': RoxyGunaMilan;\n\t}\n}\n", "import { css } from 'lit';\n\n/**\n * Shared host styles every component pulls in. Sets default font, color,\n * container query support, and the entry fade-in.\n */\nexport const baseStyles = css`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\n\t}\n`;\n"],
5
- "mappings": "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",
3
+ "sources": ["../../src/components/guna-milan.ts", "../../src/utils/base-styles.ts", "../../src/utils/format.ts"],
4
+ "sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { CompatibilityResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatNumber, formatPercent } from '../utils/format.js';\n\nconst STANDARD_CATEGORIES = [\n\t'Varna',\n\t'Vasya',\n\t'Tara',\n\t'Yoni',\n\t'Maitri',\n\t'Gana',\n\t'Bhakoot',\n\t'Nadi',\n];\n\n/**\n * 36-point Ashtakoota score card. Renders /vedic-astrology/compatibility.\n */\n@customElement('roxy-guna-milan')\nexport class RoxyGunaMilan extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`\n\t\t\t.card {\n\t\t\t\tbackground: var(--roxy-bg, #fff);\n\t\t\t\tborder: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t\t\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\t\t\tbox-shadow: var(--roxy-shadow-sm);\n\t\t\t\tdisplay: grid;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\n\t\t\t.score-bar {\n\t\t\t\tdisplay: grid;\n\t\t\t\tgrid-template-columns: 1fr auto;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: var(--roxy-space-md, 1rem);\n\t\t\t}\n\t\t\t.total {\n\t\t\t\tfont-size: 2.25rem;\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\tcolor: var(--roxy-accent-fg, #b45309);\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t\tline-height: 1;\n\t\t\t}\n\t\t\t.over {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\t\t}\n\t\t\t.recommendation {\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t\tcolor: var(--roxy-secondary, #475569);\n\t\t\t}\n\n\t\t\ttable {\n\t\t\t\twidth: 100%;\n\t\t\t\tborder-collapse: collapse;\n\t\t\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t\t\t}\n\t\t\tth,\n\t\t\ttd {\n\t\t\t\tpadding: var(--roxy-space-sm, 0.5rem);\n\t\t\t\tborder-bottom: 1px solid var(--roxy-border, #e4e4e7);\n\t\t\t\ttext-align: left;\n\t\t\t}\n\t\t\tth {\n\t\t\t\tcolor: var(--roxy-muted, #71717a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t\ttext-transform: uppercase;\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t\tletter-spacing: 0.06em;\n\t\t\t}\n\t\t\ttd.score {\n\t\t\t\ttext-align: right;\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\t\t\tfont-weight: var(--roxy-weight-bold, 600);\n\t\t\t}\n\t\t\ttd.bar-cell {\n\t\t\t\twidth: 30%;\n\t\t\t}\n\t\t\t.mini-bar {\n\t\t\t\theight: 8px;\n\t\t\t\tbackground: var(--roxy-border, #e4e4e7);\n\t\t\t\tborder-radius: var(--roxy-radius-full, 9999px);\n\t\t\t\toverflow: hidden;\n\t\t\t}\n\t\t\t.mini-bar > span {\n\t\t\t\tdisplay: block;\n\t\t\t\theight: 100%;\n\t\t\t\tbackground: var(--roxy-accent, #f59e0b);\n\t\t\t\ttransition:\n\t\t\t\t\twidth var(--roxy-motion-duration, 200ms)\n\t\t\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1));\n\t\t\t}\n\n\t\t\t.tags {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-wrap: wrap;\n\t\t\t\tgap: var(--roxy-space-xs, 0.25rem);\n\t\t\t}\n\t\t\t.tags span {\n\t\t\t\tpadding: 2px 8px;\n\t\t\t\tborder-radius: var(--roxy-radius-full, 9999px);\n\t\t\t\tfont-size: var(--roxy-text-xs, 0.75rem);\n\t\t\t}\n\t\t\t.tags .dosha {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-danger, #dc2626) 16%, transparent);\n\t\t\t\tcolor: var(--roxy-danger-fg, #991b1b);\n\t\t\t}\n\t\t\t.tags .cancel {\n\t\t\t\tbackground: color-mix(in srgb, var(--roxy-success, #16a34a) 18%, transparent);\n\t\t\t\tcolor: var(--roxy-success-fg, #166534);\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ attribute: false })\n\tdata: CompatibilityResponse | null = null;\n\n\trender() {\n\t\tconst d = this.data;\n\t\tif (!d)\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">No Guna Milan data</div>`;\n\n\t\tconst breakdown = (d.breakdown ?? []).filter(\n\t\t\t(b) => b?.category !== undefined,\n\t\t);\n\n\t\treturn html`<article class=\"card\" aria-label=\"Guna Milan score\">\n\t\t\t<div class=\"score-bar\">\n\t\t\t\t<div>\n\t\t\t\t\t<span class=\"total\">${formatNumber(d.total, 1)}</span>\n\t\t\t\t\t<span class=\"over\"> / ${d.maxScore}</span>\n\t\t\t\t\t${\n\t\t\t\t\t\ttypeof d.percentage === 'number'\n\t\t\t\t\t\t\t? html`<small style=\"margin-left: 0.5rem; color: var(--roxy-muted)\">\n\t\t\t\t\t\t\t\t${formatPercent(d.percentage, 1)}\n\t\t\t\t\t\t\t</small>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t\t${\n\t\t\t\t\td.recommendation\n\t\t\t\t\t\t? html`<span class=\"recommendation\">${d.recommendation}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t</div>\n\n\t\t\t${\n\t\t\t\tbreakdown.length > 0\n\t\t\t\t\t? html`<table>\n\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<th>Category</th>\n\t\t\t\t\t\t\t\t<th>Progress</th>\n\t\t\t\t\t\t\t\t<th class=\"score\">Score</th>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t${breakdown.map((b) => {\n\t\t\t\t\t\t\t\tconst score = b.score ?? 0;\n\t\t\t\t\t\t\t\tconst maxScore = b.maxScore ?? defaultMax(b.category);\n\t\t\t\t\t\t\t\tconst pct = maxScore ? (score / maxScore) * 100 : 0;\n\t\t\t\t\t\t\t\treturn html`<tr>\n\t\t\t\t\t\t\t\t\t<td>${b.category}</td>\n\t\t\t\t\t\t\t\t\t<td class=\"bar-cell\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"mini-bar\">\n\t\t\t\t\t\t\t\t\t\t\t<span style=\"width: ${pct}%\"></span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td class=\"score\">${formatNumber(score, 1)} / ${maxScore}</td>\n\t\t\t\t\t\t\t\t</tr>`;\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</table>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t${\n\t\t\t\t(d.doshas?.length ?? 0) > 0 || (d.doshaCancellations?.length ?? 0) > 0\n\t\t\t\t\t? html`<div class=\"tags\">\n\t\t\t\t\t\t${d.doshas?.map((x) => html`<span class=\"dosha\">${x}</span>`)}\n\t\t\t\t\t\t${d.doshaCancellations?.map(\n\t\t\t\t\t\t\t(x) =>\n\t\t\t\t\t\t\t\thtml`<span class=\"cancel\" title=${x.reason}>${x.dosha} cancelled</span>`,\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t</article>`;\n\t}\n}\n\nfunction defaultMax(name?: string): number {\n\tif (!name) return 1;\n\tswitch (name.toLowerCase()) {\n\t\tcase 'varna':\n\t\t\treturn 1;\n\t\tcase 'vasya':\n\t\t\treturn 2;\n\t\tcase 'tara':\n\t\t\treturn 3;\n\t\tcase 'yoni':\n\t\t\treturn 4;\n\t\tcase 'maitri':\n\t\t\treturn 5;\n\t\tcase 'gana':\n\t\t\treturn 6;\n\t\tcase 'bhakoot':\n\t\t\treturn 7;\n\t\tcase 'nadi':\n\t\t\treturn 8;\n\t\tdefault:\n\t\t\treturn 1;\n\t}\n}\n\nexport const GUNA_CATEGORIES = STANDARD_CATEGORIES;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-guna-milan': RoxyGunaMilan;\n\t}\n}\n", "import { css } from 'lit';\n\n/**\n * Shared host styles every component pulls in. Sets default font, color,\n * container query support, and the entry fade-in.\n */\nexport const baseStyles = css`\n\t:host {\n\t\tdisplay: block;\n\t\tcontainer-type: inline-size;\n\t\tfont-family: var(\n\t\t\t--roxy-font-sans,\n\t\t\tsystem-ui,\n\t\t\t-apple-system,\n\t\t\tBlinkMacSystemFont,\n\t\t\t'Segoe UI',\n\t\t\tRoboto,\n\t\t\tsans-serif\n\t\t);\n\t\tcolor: var(--roxy-fg, #0a0a0a);\n\t\tbackground: transparent;\n\t\tfont-size: var(--roxy-text-base, 1rem);\n\t\tline-height: var(--roxy-leading-normal, 1.5);\n\t\tanimation: roxy-fade-in var(--roxy-motion-duration, 200ms)\n\t\t\tvar(--roxy-motion-easing, cubic-bezier(0.4, 0, 0.2, 1)) both;\n\t}\n\n\t*,\n\t*::before,\n\t*::after {\n\t\tbox-sizing: border-box;\n\t}\n\n\t@keyframes roxy-fade-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translateY(2px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translateY(0);\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t:host {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-skeleton {\n\t\tbackground: linear-gradient(\n\t\t\t90deg,\n\t\t\tvar(--roxy-border, #e4e4e7) 0%,\n\t\t\tcolor-mix(in srgb, var(--roxy-border, #e4e4e7) 60%, transparent) 50%,\n\t\t\tvar(--roxy-border, #e4e4e7) 100%\n\t\t);\n\t\tbackground-size: 200% 100%;\n\t\tanimation: roxy-shimmer 1.4s ease-in-out infinite;\n\t\tborder-radius: var(--roxy-radius-md, 8px);\n\t}\n\n\t@keyframes roxy-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 200% 0;\n\t\t}\n\t\t100% {\n\t\t\tbackground-position: -200% 0;\n\t\t}\n\t}\n\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.roxy-skeleton {\n\t\t\tanimation: none;\n\t\t}\n\t}\n\n\t.roxy-empty {\n\t\tpadding: var(--roxy-space-lg, 1.5rem);\n\t\tcolor: var(--roxy-muted, #71717a);\n\t\ttext-align: center;\n\t\tfont-size: var(--roxy-text-sm, 0.875rem);\n\t}\n\n\t:host(:focus-within) .roxy-card {\n\t\toutline: 2px solid var(--roxy-ring, rgba(245, 158, 11, 0.4));\n\t\toutline-offset: 2px;\n\t}\n`;\n", "/**\n * Display formatters for ISO timestamps and floats coming back from the API.\n * Every helper returns \"\" for nullish or unparseable input so it falls out of\n * template literals cleanly.\n */\n\nexport function formatTime(input: unknown): string {\n\tif (typeof input !== 'string' || input.length === 0) return '';\n\tif (/^\\d{4}-\\d{2}-\\d{2}$/.test(input)) return '';\n\tconst bareTime = /^\\d{2}:\\d{2}(:\\d{2})?$/.test(input);\n\tconst iso = bareTime ? `1970-01-01T${input}` : input;\n\tconst d = new Date(iso);\n\tif (Number.isNaN(d.getTime())) return input;\n\treturn d.toLocaleTimeString(undefined, {\n\t\thour: 'numeric',\n\t\tminute: '2-digit',\n\t\thour12: true,\n\t});\n}\n\nexport function formatDate(input: unknown): string {\n\tif (typeof input !== 'string' || input.length === 0) return '';\n\tconst d = new Date(\n\t\t/^\\d{4}-\\d{2}-\\d{2}$/.test(input) ? `${input}T00:00:00` : input,\n\t);\n\tif (Number.isNaN(d.getTime())) return input;\n\treturn d.toLocaleDateString(undefined, {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\tyear: 'numeric',\n\t});\n}\n\nexport function formatTimeRange(\n\tt: { start?: string; end?: string } | undefined,\n): string {\n\tif (!t) return '';\n\tconst start = formatTime(t.start);\n\tconst end = formatTime(t.end);\n\tif (start && end) return `${start} - ${end}`;\n\treturn start || end || '';\n}\n\nexport function formatNumber(value: unknown, dp = 1): string {\n\tif (typeof value !== 'number' || !Number.isFinite(value)) return '';\n\treturn value.toFixed(dp).replace(/\\.?0+$/, '');\n}\n\nexport function formatPercent(value: unknown, dp = 1): string {\n\tconst n = formatNumber(value, dp);\n\treturn n ? `${n}%` : '';\n}\n\nexport function formatLongitude(value: unknown): string {\n\tif (typeof value !== 'number' || !Number.isFinite(value)) return '';\n\treturn `${formatNumber(value, 2)}\u00B0`;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;AAAA,SAAS,OAAAA,MAAK,MAAM,YAAY,eAAe;AAC/C,SAAS,eAAe,gBAAgB;;;ACDxC,SAAS,WAAW;AAMb,IAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACqCnB,SAAS,aAAa,OAAgB,KAAK,GAAW;AAC5D,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,SAAO,MAAM,QAAQ,EAAE,EAAE,QAAQ,UAAU,EAAE;AAC9C;AAEO,SAAS,cAAc,OAAgB,KAAK,GAAW;AAC7D,QAAM,IAAI,aAAa,OAAO,EAAE;AAChC,SAAO,IAAI,GAAG,CAAC,MAAM;AACtB;;;AF7CA,IAAM,sBAAsB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAMO,IAAM,gBAAN,cAA4B,WAAW;AAAA,EAAvC;AAAA;AAoGN,gBAAqC;AAAA;AAAA,EAErC,SAAS;AACR,UAAM,IAAI,KAAK;AACf,QAAI,CAAC;AACJ,aAAO;AAER,UAAM,aAAa,EAAE,aAAa,CAAC,GAAG;AAAA,MACrC,CAAC,MAAM,GAAG,aAAa;AAAA,IACxB;AAEA,WAAO;AAAA;AAAA;AAAA,2BAGkB,aAAa,EAAE,OAAO,CAAC,CAAC;AAAA,6BACtB,EAAE,QAAQ;AAAA,OAEjC,OAAO,EAAE,eAAe,WACrB;AAAA,UACC,cAAc,EAAE,YAAY,CAAC,CAAC;AAAA,mBAE/B,OACJ;AAAA;AAAA,MAGA,EAAE,iBACC,oCAAoC,EAAE,cAAc,YACpD,OACJ;AAAA;AAAA;AAAA,KAIA,UAAU,SAAS,IAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SASE,UAAU,IAAI,CAAC,MAAM;AACtB,YAAM,QAAQ,EAAE,SAAS;AACzB,YAAM,WAAW,EAAE,YAAY,WAAW,EAAE,QAAQ;AACpD,YAAM,MAAM,WAAY,QAAQ,WAAY,MAAM;AAClD,aAAO;AAAA,eACA,EAAE,QAAQ;AAAA;AAAA;AAAA,iCAGQ,GAAG;AAAA;AAAA;AAAA,6BAGP,aAAa,OAAO,CAAC,CAAC,MAAM,QAAQ;AAAA;AAAA,IAE1D,CAAC,CAAC;AAAA;AAAA,iBAGF,OACJ;AAAA,MAEE,EAAE,QAAQ,UAAU,KAAK,MAAM,EAAE,oBAAoB,UAAU,KAAK,IAClE;AAAA,QACC,EAAE,QAAQ,IAAI,CAAC,MAAM,2BAA2B,CAAC,SAAS,CAAC;AAAA,QAC3D,EAAE,oBAAoB;AAAA,MACvB,CAAC,MACA,kCAAkC,EAAE,MAAM,IAAI,EAAE,KAAK;AAAA,IACvD,CAAC;AAAA,eAEA,OACJ;AAAA;AAAA,EAEF;AACD;AA7Ka,cACL,SAAS;AAAA,EACf;AAAA,EACAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8FD;AAGA;AAAA,EADC,SAAS,EAAE,WAAW,MAAM,CAAC;AAAA,GAnGlB,cAoGZ;AApGY,gBAAN;AAAA,EADN,cAAc,iBAAiB;AAAA,GACnB;AA+Kb,SAAS,WAAW,MAAuB;AAC1C,MAAI,CAAC,KAAM,QAAO;AAClB,UAAQ,KAAK,YAAY,GAAG;AAAA,IAC3B,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR;AACC,aAAO;AAAA,EACT;AACD;AAEO,IAAM,kBAAkB;",
6
6
  "names": ["css", "css"]
7
7
  }
@@ -1,30 +1,6 @@
1
1
  import { LitElement } from 'lit';
2
- interface HexagramData {
3
- number?: number;
4
- symbol?: string;
5
- chinese?: string;
6
- english?: string;
7
- pinyin?: string;
8
- upperTrigram?: string;
9
- lowerTrigram?: string;
10
- judgment?: string;
11
- image?: string;
12
- interpretation?: {
13
- general?: string;
14
- love?: string;
15
- career?: string;
16
- decision?: string;
17
- advice?: string;
18
- };
19
- changingLines?: number[];
20
- resultingHexagram?: HexagramData;
21
- dailyMessage?: string;
22
- hexagram?: HexagramData;
23
- lines?: number[];
24
- changingLinePositions?: number[];
25
- seed?: string;
26
- date?: string;
27
- }
2
+ import type { CastReadingResponse, GetDailyHexagramResponse, GetHexagramResponse, GetRandomHexagramResponse, LookupHexagramResponse } from '../types/index.js';
3
+ type HexagramData = GetHexagramResponse | GetRandomHexagramResponse | LookupHexagramResponse | GetDailyHexagramResponse | CastReadingResponse;
28
4
  /**
29
5
  * I Ching hexagram card. Renders /iching/hexagrams/{number}, /iching/cast,
30
6
  * /iching/daily, /iching/daily/cast.
@@ -33,7 +9,7 @@ export declare class RoxyHexagram extends LitElement {
33
9
  static styles: import("lit").CSSResult[];
34
10
  data: HexagramData | null;
35
11
  mode: 'lookup' | 'cast' | 'daily';
36
- private getHexagram;
12
+ private resolveHexagram;
37
13
  render(): import("lit").TemplateResult<1>;
38
14
  /** When the API only ships symbol+number with no line array, render six solid yang. */
39
15
  private derivedLines;
@@ -1 +1 @@
1
- {"version":3,"file":"hexagram.d.ts","sourceRoot":"","sources":["../../src/components/hexagram.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAgB,MAAM,KAAK,CAAC;AAK1D,UAAU,YAAY;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,iBAAiB,CAAC,EAAE,YAAY,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,qBACa,YAAa,SAAQ,UAAU;IAC3C,MAAM,CAAC,MAAM,4BA8GX;IAGF,IAAI,EAAE,YAAY,GAAG,IAAI,CAAQ;IAGjC,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAY;IAE7C,OAAO,CAAC,WAAW;IAYnB,MAAM;IAwFN,uFAAuF;IACvF,OAAO,CAAC,YAAY;CAepB;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,eAAe,EAAE,YAAY,CAAC;KAC9B;CACD"}
1
+ {"version":3,"file":"hexagram.d.ts","sourceRoot":"","sources":["../../src/components/hexagram.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAgB,MAAM,KAAK,CAAC;AAG1D,OAAO,KAAK,EACX,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,yBAAyB,EAEzB,sBAAsB,EACtB,MAAM,mBAAmB,CAAC;AAG3B,KAAK,YAAY,GACd,mBAAmB,GACnB,yBAAyB,GACzB,sBAAsB,GACtB,wBAAwB,GACxB,mBAAmB,CAAC;AAEvB;;;GAGG;AACH,qBACa,YAAa,SAAQ,UAAU;IAC3C,MAAM,CAAC,MAAM,4BA8GX;IAGF,IAAI,EAAE,YAAY,GAAG,IAAI,CAAQ;IAGjC,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAY;IAE7C,OAAO,CAAC,eAAe;IA4BvB,MAAM;IA+FN,uFAAuF;IACvF,OAAO,CAAC,YAAY;CAcpB;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,eAAe,EAAE,YAAY,CAAC;KAC9B;CACD"}
@@ -126,23 +126,40 @@ var RoxyHexagram = class extends LitElement {
126
126
  this.data = null;
127
127
  this.mode = "lookup";
128
128
  }
129
- getHexagram() {
130
- if (!this.data) return null;
131
- if ("hexagram" in this.data && this.data.hexagram) {
129
+ resolveHexagram() {
130
+ const d = this.data;
131
+ if (!d) return null;
132
+ if ("hexagram" in d && d.hexagram) {
133
+ if ("lines" in d) {
134
+ const cast = d;
135
+ return {
136
+ hex: cast.hexagram,
137
+ lines: cast.lines,
138
+ changingLinePositions: cast.changingLinePositions,
139
+ resultingHexagram: cast.resultingHexagram
140
+ };
141
+ }
142
+ const daily = d;
132
143
  return {
133
- ...this.data.hexagram,
134
- lines: this.data.lines,
135
- changingLinePositions: this.data.changingLinePositions
144
+ hex: daily.hexagram,
145
+ dailyMessage: daily.dailyMessage
136
146
  };
137
147
  }
138
- return this.data;
148
+ return { hex: d };
139
149
  }
140
150
  render() {
141
- const h = this.getHexagram();
142
- if (!h)
151
+ const resolved = this.resolveHexagram();
152
+ if (!resolved)
143
153
  return html`<div class="roxy-empty" role="status">No hexagram data</div>`;
144
- const lines = h.lines ?? this.derivedLines(h);
145
- const changing = new Set(h.changingLinePositions ?? []);
154
+ const {
155
+ hex: h,
156
+ lines: castLines,
157
+ changingLinePositions,
158
+ dailyMessage,
159
+ resultingHexagram
160
+ } = resolved;
161
+ const lines = castLines ?? this.derivedLines(h);
162
+ const changing = new Set(changingLinePositions ?? []);
146
163
  return html`<article class="card" aria-label="I Ching hexagram">
147
164
  <div class="glyphs">
148
165
  ${h.symbol ? html`<div class="symbol">${h.symbol}</div>` : nothing}
@@ -182,19 +199,18 @@ var RoxyHexagram = class extends LitElement {
182
199
  </div>
183
200
  ${h.judgment ? html`<p class="judgment">${h.judgment}</p>` : nothing}
184
201
  ${h.image ? html`<p class="image">${h.image}</p>` : nothing}
185
- ${h.dailyMessage ? html`<p class="message">${h.dailyMessage}</p>` : nothing}
202
+ ${dailyMessage ? html`<p class="message">${dailyMessage}</p>` : nothing}
186
203
  ${h.interpretation?.general ? html`<p>${h.interpretation.general}</p>` : nothing}
187
204
  ${changing.size > 0 ? html`<div class="changing">
188
205
  Changing lines: ${Array.from(changing).sort((a, b) => a - b).join(", ")}.
189
- ${h.resultingHexagram?.english ? html` Becomes hexagram ${h.resultingHexagram.number}
190
- ${h.resultingHexagram.english}.` : nothing}
206
+ ${resultingHexagram?.english ? html` Becomes hexagram ${resultingHexagram.number}
207
+ ${resultingHexagram.english}.` : nothing}
191
208
  </div>` : nothing}
192
209
  </div>
193
210
  </article>`;
194
211
  }
195
212
  /** When the API only ships symbol+number with no line array, render six solid yang. */
196
213
  derivedLines(h) {
197
- if (!h.symbol) return Array.from({ length: 6 }, () => 7);
198
214
  const cp = h.symbol.codePointAt(0) ?? 0;
199
215
  if (cp >= 19904 && cp <= 19967) {
200
216
  const offset = cp - 19904;