@roxyapi/ui 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +35 -52
- package/README.md +99 -41
- package/dist/cdn/components/bodygraph.js +54 -0
- package/dist/cdn/components/bodygraph.js.map +7 -0
- package/dist/cdn/components/forecast-timeline.js +45 -0
- package/dist/cdn/components/forecast-timeline.js.map +7 -0
- package/dist/cdn/roxy-ui.js +49 -40
- package/dist/cdn/roxy-ui.js.map +4 -4
- package/dist/components/bodygraph.d.ts +27 -0
- package/dist/components/bodygraph.d.ts.map +1 -0
- package/dist/components/bodygraph.js +11 -0
- package/dist/components/bodygraph.js.map +7 -0
- package/dist/components/forecast-timeline.d.ts +38 -0
- package/dist/components/forecast-timeline.d.ts.map +1 -0
- package/dist/components/forecast-timeline.js +2 -0
- package/dist/components/forecast-timeline.js.map +7 -0
- package/dist/index.cjs +45 -36
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -43
- package/dist/index.js.map +4 -4
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.json +2 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types.gen.d.ts +3994 -6
- package/dist/types/types.gen.d.ts.map +1 -1
- package/dist/utils/bodygraph-render.d.ts +105 -0
- package/dist/utils/bodygraph-render.d.ts.map +1 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/bodygraph.ts +390 -0
- package/src/components/forecast-timeline.ts +336 -0
- package/src/index.ts +4 -0
- package/src/manifest.ts +26 -0
- package/src/types/index.ts +1 -1
- package/src/types/types.gen.ts +4079 -6
- package/src/utils/bodygraph-render.ts +641 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import type { GenerateBodygraphResponse } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Human Design bodygraph. Pass `data` from /human-design/bodygraph. Renders the
|
|
5
|
+
* nine centers in their canonical positions and shapes, filled when defined and
|
|
6
|
+
* outlined when open, the 36 channels as wiring between gates with active
|
|
7
|
+
* channels emphasized, and the activated gate numbers. A summary block lists
|
|
8
|
+
* type, strategy, authority, profile, definition, incarnation cross, signature,
|
|
9
|
+
* and not-self theme.
|
|
10
|
+
*
|
|
11
|
+
* The chart is theme-driven through `--roxy-*` custom properties on `:host`, so
|
|
12
|
+
* it adopts the host palette in light and dark without runtime color probing.
|
|
13
|
+
*/
|
|
14
|
+
export declare class RoxyBodygraph extends LitElement {
|
|
15
|
+
static styles: import("lit").CSSResult[];
|
|
16
|
+
constructor();
|
|
17
|
+
data: GenerateBodygraphResponse | null;
|
|
18
|
+
render(): import("lit").TemplateResult<1>;
|
|
19
|
+
private buildGateTitles;
|
|
20
|
+
private renderSummary;
|
|
21
|
+
}
|
|
22
|
+
declare global {
|
|
23
|
+
interface HTMLElementTagNameMap {
|
|
24
|
+
'roxy-bodygraph': RoxyBodygraph;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=bodygraph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bodygraph.d.ts","sourceRoot":"","sources":["../../src/components/bodygraph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAGrD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAanE;;;;;;;;;;GAUG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC5C,MAAM,CAAC,MAAM,4BA8NX;;IAWF,IAAI,EAAE,yBAAyB,GAAG,IAAI,CAAQ;IAE9C,MAAM;IAsDN,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,aAAa;CAkDrB;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,aAAa,CAAC;KAChC;CACD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var L=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var v=(t,e,r,a)=>{for(var n=a>1?void 0:a?B(e,r):e,o=t.length-1,s;o>=0;o--)(s=t[o])&&(n=(a?s(e,r,n):s(n))||n);return a&&n&&L(e,r,n),n};import{css as re,html as c,LitElement as te,nothing as p}from"lit";import{customElement as ne,property as ae}from"lit/decorators.js";var R={Sun:"\u2609",Moon:"\u263D",Mercury:"\u263F",Venus:"\u2640",Earth:"\u2641",Mars:"\u2642",Jupiter:"\u2643",Saturn:"\u2644",Uranus:"\u2645",Neptune:"\u2646",Pluto:"\u2647",Rahu:"\u260A",Ketu:"\u260B",Ascendant:"Asc",Lagna:"La",NorthNode:"\u260A",SouthNode:"\u260B","North node":"\u260A","South node":"\u260B",Chiron:"\u26B7",Lilith:"\u26B8","Black moon lilith":"\u26B8"};var _=["Aries","Taurus","Gemini","Cancer","Leo","Virgo","Libra","Scorpio","Sagittarius","Capricorn","Aquarius","Pisces"],se=_.map(t=>t.toLowerCase());import{css as O}from"lit";var C=O`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;import{nothing as M,svg as d}from"lit";var x=4,f=9,T=(100+2*f)*x,H=(100+2*f)*x;function l(t,e){return{x:(t+f)*x,y:(e+f)*x}}var m=50,w=t=>2*m-t,$={head:{64:[45.5,11.2],61:[50,11.2],63:[54.5,11.2]},ajna:{47:[45.3,18],24:[50,18],4:[54.7,18],17:[45.6,20.6],11:[54.4,20.6],43:[50,25.3]},throat:{62:[45.5,32.3],23:[50,32.3],56:[54.5,32.3],16:[42,34.6],35:[58,34.6],12:[58,37.6],20:[42,40.6],45:[58,40.6],31:[46,42.4],8:[50,42.4],33:[54,42.4]},g:{1:[50,47.5],7:[45.6,50.3],13:[54.4,50.3],10:[40,53.3],25:[60,53.3],15:[45.6,56.6],46:[54.4,56.6],2:[50,59]},heart:{21:[62.5,58.5],51:[62.5,61.3],26:[62.5,64.1],40:[73,61.3]},spleen:{48:[20,70.6],57:[24,72.3],44:[28,74],50:[32,75.6],32:[28,77.2],28:[24,78.9],18:[20,80.6]},sacral:{5:[45.5,72.5],14:[50,72.5],29:[54.5,72.5],34:[42,75.6],27:[42,79],59:[58,79],42:[45.5,81.4],3:[50,81.4],9:[54.5,81.4]},"solar-plexus":{},root:{53:[45.5,89.9],60:[50,89.9],52:[54.5,89.9],54:[42,92.7],19:[58,92.7],38:[42,95.3],39:[58,95.3],58:[42,98],41:[58,98]}},j={48:36,57:22,44:37,50:6,32:49,28:55,18:30};function z(){let t={},e={};for(let[r,[a,n]]of Object.entries($.spleen))$["solar-plexus"][j[Number(r)]]=[w(a),n];for(let[r,a]of Object.entries($))for(let[n,[o,s]]of Object.entries(a))t[Number(n)]=l(o,s),e[Number(n)]=r;return{points:t,centerOf:e}}var{points:S,centerOf:pe}=z(),ue=T/2;function u(t){return t.map(([e,r])=>l(e,r))}function A(t,e,r){return u([[m-t,e],[m+t,e],[m+t,r],[m-t,r]])}var P=[[18.4,68],[18.4,81.8],[34.7,74.9]],Y=[{id:"head",label:"Head",color:"gold",points:u([[40,14.3],[60,14.3],[50,6]]),labelAnchor:l(63,9),labelAlign:"start"},{id:"ajna",label:"Ajna",color:"green",points:u([[40,15.6],[60,15.6],[50,27.6]]),labelAnchor:l(62,21),labelAlign:"start"},{id:"throat",label:"Throat",color:"brown",points:A(9.5,30.4,43.6),labelAnchor:l(83,34),labelAlign:"start"},{id:"g",label:"G",color:"gold",points:u([[50,45],[60.7,53.3],[50,61.6],[39.3,53.3]]),labelAnchor:l(13,51),labelAlign:"end"},{id:"heart",label:"Heart",color:"red",points:u([[61.5,57],[76.5,61.3],[61.5,65.6]]),labelAnchor:l(85,56),labelAlign:"start"},{id:"spleen",label:"Spleen",color:"brown",points:u(P),labelAnchor:l(13,70),labelAlign:"end"},{id:"sacral",label:"Sacral",color:"red",points:A(9.5,70.6,83.6),labelAnchor:l(85,88),labelAlign:"start"},{id:"solar-plexus",label:"Solar Plexus",color:"brown",points:u(P.map(([t,e])=>[w(t),e])),labelAnchor:l(87,73),labelAlign:"start"},{id:"root",label:"Root",color:"brown",points:A(9.5,87.9,99.9),labelAnchor:l(50,103),labelAlign:"middle"}],D=[[64,47],[61,24],[63,4],[17,62],[11,56],[43,23],[16,48],[20,34],[20,10],[7,31],[1,8],[13,33],[21,45],[12,22],[35,36],[57,20],[15,5],[2,14],[46,29],[34,10],[10,57],[25,51],[27,50],[57,34],[26,44],[18,58],[28,38],[32,54],[3,60],[9,52],[42,53],[59,6],[19,49],[39,55],[41,30],[37,40]],V=q();function q(){let t=w,e=[[50,-2],[60,-2],[60.5,9],[57,18],[56,21],[54,24],[52,27],[54,28],[64,30],[80,34],[83.5,40],[84,56],[83,74],[82,84],[76,92],[68,97],[64,99],[62,100],[60,100]],r=[`M ${b(e[0])}`];for(let a=1;a+2<e.length;a+=3)r.push(`C ${b(e[a])} ${b(e[a+1])} ${b(e[a+2])}`);r.push(`L ${y(e[e.length-1],t)}`);for(let a=e.length-3;a>=1;a-=3)r.push(`C ${y(e[a+1],t)} ${y(e[a],t)} ${y(e[a-1],t)}`);return r.push("Z"),r.join(" ")}function b([t,e]){let r=l(t,e);return`${r.x} ${r.y}`}function y([t,e],r){let a=l(r(t),e);return`${a.x} ${a.y}`}function U(t){return t.map(e=>`${e.x},${e.y}`).join(" ")}function k(t,e){return t<e?`${t}-${e}`:`${e}-${t}`}function X(){return d`<path class="bg-body" d=${V} />`}function K(t,e){let r=[];for(let[a,n]of D){let o=S[a],s=S[n];if(!o||!s)continue;if(r.push(d`<line class="bg-channel" x1=${o.x} y1=${o.y} x2=${s.x} y2=${s.y} />`),t.has(k(a,n))){r.push(d`<line class="bg-channel on" x1=${o.x} y1=${o.y} x2=${s.x} y2=${s.y} />`);continue}let i={x:(o.x+s.x)/2,y:(o.y+s.y)/2};e.has(a)&&r.push(d`<line class="bg-half" x1=${o.x} y1=${o.y} x2=${i.x} y2=${i.y} />`),e.has(n)&&r.push(d`<line class="bg-half" x1=${s.x} y1=${s.y} x2=${i.x} y2=${i.y} />`)}return r}function J(t,e,r){let a=r.x-e.x,n=r.y-e.y,o=a*a+n*n;if(o===0)return e;let s=Math.max(0,Math.min(1,((t.x-e.x)*a+(t.y-e.y)*n)/o));return{x:e.x+s*a,y:e.y+s*n}}function W(t,e){let r=e[0],a=Number.POSITIVE_INFINITY;for(let n=0;n<e.length;n++){let o=J(t,e[n],e[(n+1)%e.length]),s=(o.x-t.x)**2+(o.y-t.y)**2;s<a&&(a=s,r=o)}return r}function F(t){return Y.map(e=>{let r=t.has(e.id),a=`bg-center bg-${e.color}${r?" defined":""}`,n=W(e.labelAnchor,e.points);return d`<g>
|
|
2
|
+
<line class="bg-leader" x1=${e.labelAnchor.x} y1=${e.labelAnchor.y} x2=${n.x} y2=${n.y} />
|
|
3
|
+
<polygon class=${a} points=${U(e.points)}><title>${e.label}: ${r?"defined":"open"}</title></polygon>
|
|
4
|
+
<text class="bg-center-label" x=${e.labelAnchor.x} y=${e.labelAnchor.y} text-anchor=${e.labelAlign} dominant-baseline="central">${e.label}</text>
|
|
5
|
+
</g>`})}function Z(t,e){let r=[];for(let[a,n]of Object.entries(S)){let o=Number(a);if(!t.has(o))continue;let s=e.get(o);r.push(d`<text class="bg-gate" x=${n.x} y=${n.y} text-anchor="middle" dominant-baseline="central">${o}${s?d`<title>${s}</title>`:M}</text>`)}return r}function E(t,e){return k(t,e)}var N=`0 0 ${T} ${H}`;function G(t){return d`
|
|
6
|
+
${X()}
|
|
7
|
+
${K(t.activeChannels,t.activeGates)}
|
|
8
|
+
${F(t.definedCenters)}
|
|
9
|
+
${Z(t.activeGates,t.gateTitles)}
|
|
10
|
+
`}var Q="roxy-data";function ee(t){return t.nodeName==="SCRIPT"&&t.getAttribute("type")==="application/json"}var h=class{constructor(e){this.host=e,e.addController(this)}hostConnected(){if(this.host.data!=null)return;let e=this.read();e!==void 0&&(this.host.data=e,this.host.requestUpdate())}read(){let e=this.findInlineScript();return e?this.parse(e.textContent):void 0}findInlineScript(){for(let e of Array.from(this.host.children))if(ee(e)&&e.classList.contains(Q))return e;return null}parse(e){if(e?.trim())try{return JSON.parse(e)}catch{return}}};function I(t){return t?t.charAt(0).toUpperCase()+t.slice(1).toLowerCase():""}var g=class extends te{constructor(){super();this.data=null;new h(this)}render(){let r=this.data;if(!r)return c`<div class="roxy-empty" role="status">No bodygraph data</div>`;let a=new Set((r.centers??[]).filter(i=>i.defined).map(i=>i.id)),n=new Set((r.gates??[]).map(i=>i.gate).filter(i=>i!=null)),o=new Set((r.channels??[]).map(i=>E(i.gateA,i.gateB))),s=this.buildGateTitles(r.gates??[]);return c`<div class="wrap"><header class="head"><h2 class="title">Bodygraph</h2>${r.type?c`<div class="type-line">${r.type}${r.profile?c`· Profile ${r.profile}`:p}</div>`:p}</header><div class="layout"><svg viewBox="${N}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Human Design bodygraph with nine centers, channels, and activated gates overlaid on a human silhouette"><title>Human Design bodygraph</title><desc>Nine energy centers in their canonical positions over a human silhouette, each filled with its traditional color when defined and outlined when open, wired by channels between activated gates.</desc>${G({definedCenters:a,activeChannels:o,activeGates:n,gateTitles:s})}</svg> ${this.renderSummary(r)}</div></div>`}buildGateTitles(r){let a=new Map;for(let n of r){if(n.gate==null)continue;let o=[`Gate ${n.gate}`];n.line!=null&&(o[0]+=`.${n.line}`),n.gateName&&o.push(n.gateName);let s=n.planet?I(n.planet):"",i=s?R[s]??s:"";i&&o.push(`${i} ${n.side??""}`.trim()),a.set(n.gate,o.join(" \xB7 "))}return a}renderSummary(r){let a=[{label:"Type",value:r.type},{label:"Strategy",value:r.strategy},{label:"Authority",value:r.authority},{label:"Profile",value:r.profile},{label:"Definition",value:r.definition}],n=r.incarnationCross;return c`<div class="summary"><div class="facts">${a.map(o=>o.value?c`<div class="fact"><span>${o.label}</span> <strong>${o.value}</strong></div>`:p)}</div>${n?.name?c`<p class="cross">${n.name} ${n.gates?.length?c`<span class="gates">(${n.gates.join(", ")})</span>`:p}</p>`:p} ${r.signature||r.notSelf?c`<div class="themes">${r.signature?c`<span class="pill pill--good">Signature: ${r.signature}</span>`:p} ${r.notSelf?c`<span class="pill pill--shadow">Not-self: ${r.notSelf}</span>`:p}</div>`:p}<div class="legend"><span class="legend-caption">Center colors when defined. Open centers are outlined.</span> <span><span class="swatch bg-gold defined"></span>Head, G</span> <span><span class="swatch bg-green defined"></span>Ajna</span> <span><span class="swatch bg-brown defined"></span>Throat, Spleen, Solar Plexus, Root</span> <span><span class="swatch bg-red defined"></span>Heart, Sacral</span> <span><span class="swatch"></span>Open center</span></div></div>`}};g.styles=[C,re`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.type-line{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.layout{gap:var(--roxy-space-lg,1.5rem);grid-template-columns:minmax(0,1fr);align-items:start;display:grid}@container (width>=520px){.layout{grid-template-columns:minmax(0,340px) minmax(0,1fr)}}svg{width:100%;max-width:340px;height:auto;margin:0 auto;display:block}.bg-body{fill:color-mix(in srgb, var(--roxy-secondary,#475569) 8%, transparent);stroke:var(--roxy-border,#e4e4e7);stroke-width:1px}.bg-channel{stroke:var(--roxy-secondary,#475569);stroke-width:1.6px;opacity:.3}.bg-channel.on{stroke-width:3.4px;stroke-linecap:round;opacity:1}.bg-half{stroke:var(--roxy-secondary,#475569);stroke-width:3.2px;stroke-linecap:round;opacity:.9}.bg-leader{stroke:var(--roxy-muted,#71717a);stroke-width:1px;opacity:.5}.bg-center{fill:#0000;stroke:var(--roxy-secondary,#475569);stroke-width:1.8px}.bg-center.defined{stroke:#00000073}.bg-center.bg-gold.defined{fill:#e0a200}.bg-center.bg-green.defined{fill:#2f8f00}.bg-center.bg-red.defined{fill:#c41f1f}.bg-center.bg-brown.defined{fill:#76502f}.bg-center-label{fill:var(--roxy-muted,#71717a);font-size:11px;font-family:var(--roxy-font-sans)}.bg-gate{fill:var(--roxy-fg,#0a0a0a);font-size:8px;font-weight:600;font-family:var(--roxy-font-sans);paint-order:stroke;stroke:var(--roxy-bg,#fff);stroke-width:1.6px;stroke-linejoin:round}.summary{gap:var(--roxy-space-md,1rem);display:grid}.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.fact{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);background:var(--roxy-bg,#fff)}.fact span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.fact strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.cross{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);border-left:2px solid var(--roxy-accent,#f59e0b);padding-left:var(--roxy-space-sm,.5rem);margin:0}.cross .gates{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.themes{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 10px}.pill--good{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.pill--shadow{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.legend{gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend-caption{color:var(--roxy-muted,#71717a);flex-basis:100%}.legend .swatch{vertical-align:middle;border:1px solid var(--roxy-secondary,#475569);border-radius:2px;width:10px;height:10px;margin-right:4px;display:inline-block}.legend .swatch.defined{border-color:#00000073}.legend .swatch.bg-gold{background:#e0a200}.legend .swatch.bg-green{background:#2f8f00}.legend .swatch.bg-red{background:#c41f1f}.legend .swatch.bg-brown{background:#76502f}`],v([ae({attribute:!1})],g.prototype,"data",2),g=v([ne("roxy-bodygraph")],g);export{g as RoxyBodygraph};
|
|
11
|
+
//# sourceMappingURL=bodygraph.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/components/bodygraph.ts", "../../src/tokens/index.ts", "../../src/utils/base-styles.ts", "../../src/utils/bodygraph-render.ts", "../../src/utils/markup-data.ts", "../../src/utils/string.ts"],
|
|
4
|
+
"sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { PLANET_GLYPH } from '../tokens/index.js';\nimport type { GenerateBodygraphResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tBODYGRAPH_VIEWBOX,\n\ttype BodygraphCenterId,\n\tchannelKey,\n\trenderBodygraphSvg,\n} from '../utils/bodygraph-render.js';\nimport { MarkupDataController } from '../utils/markup-data.js';\nimport { capitalize } from '../utils/string.js';\n\ntype GateActivation = GenerateBodygraphResponse['gates'][number];\n\n/**\n * Human Design bodygraph. Pass `data` from /human-design/bodygraph. Renders the\n * nine centers in their canonical positions and shapes, filled when defined and\n * outlined when open, the 36 channels as wiring between gates with active\n * channels emphasized, and the activated gate numbers. A summary block lists\n * type, strategy, authority, profile, definition, incarnation cross, signature,\n * and not-self theme.\n *\n * The chart is theme-driven through `--roxy-*` custom properties on `:host`, so\n * it adopts the host palette in light and dark without runtime color probing.\n */\n@customElement('roxy-bodygraph')\nexport class RoxyBodygraph extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.type-line{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.layout{gap:var(--roxy-space-lg,1.5rem);grid-template-columns:minmax(0,1fr);align-items:start;display:grid}@container (width>=520px){.layout{grid-template-columns:minmax(0,340px) minmax(0,1fr)}}svg{width:100%;max-width:340px;height:auto;margin:0 auto;display:block}.bg-body{fill:color-mix(in srgb, var(--roxy-secondary,#475569) 8%, transparent);stroke:var(--roxy-border,#e4e4e7);stroke-width:1px}.bg-channel{stroke:var(--roxy-secondary,#475569);stroke-width:1.6px;opacity:.3}.bg-channel.on{stroke-width:3.4px;stroke-linecap:round;opacity:1}.bg-half{stroke:var(--roxy-secondary,#475569);stroke-width:3.2px;stroke-linecap:round;opacity:.9}.bg-leader{stroke:var(--roxy-muted,#71717a);stroke-width:1px;opacity:.5}.bg-center{fill:#0000;stroke:var(--roxy-secondary,#475569);stroke-width:1.8px}.bg-center.defined{stroke:#00000073}.bg-center.bg-gold.defined{fill:#e0a200}.bg-center.bg-green.defined{fill:#2f8f00}.bg-center.bg-red.defined{fill:#c41f1f}.bg-center.bg-brown.defined{fill:#76502f}.bg-center-label{fill:var(--roxy-muted,#71717a);font-size:11px;font-family:var(--roxy-font-sans)}.bg-gate{fill:var(--roxy-fg,#0a0a0a);font-size:8px;font-weight:600;font-family:var(--roxy-font-sans);paint-order:stroke;stroke:var(--roxy-bg,#fff);stroke-width:1.6px;stroke-linejoin:round}.summary{gap:var(--roxy-space-md,1rem);display:grid}.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.fact{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);background:var(--roxy-bg,#fff)}.fact span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.fact strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.cross{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);border-left:2px solid var(--roxy-accent,#f59e0b);padding-left:var(--roxy-space-sm,.5rem);margin:0}.cross .gates{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.themes{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 10px}.pill--good{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.pill--shadow{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.legend{gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend-caption{color:var(--roxy-muted,#71717a);flex-basis:100%}.legend .swatch{vertical-align:middle;border:1px solid var(--roxy-secondary,#475569);border-radius:2px;width:10px;height:10px;margin-right:4px;display:inline-block}.legend .swatch.defined{border-color:#00000073}.legend .swatch.bg-gold{background:#e0a200}.legend .swatch.bg-green{background:#2f8f00}.legend .swatch.bg-red{background:#c41f1f}.legend .swatch.bg-brown{background:#76502f}`,\n\t];\n\n\tconstructor() {\n\t\tsuper();\n\t\t// Enables hydrating `data` from a direct-child\n\t\t// <script type=\"application/json\" class=\"roxy-data\"> for server-rendered\n\t\t// and cached consumers. The JavaScript `data` property still wins.\n\t\tnew MarkupDataController(this);\n\t}\n\n\t@property({ attribute: false })\n\tdata: GenerateBodygraphResponse | 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 bodygraph data</div>`;\n\n\t\tconst definedCenters = new Set<BodygraphCenterId>(\n\t\t\t(d.centers ?? [])\n\t\t\t\t.filter((c) => c.defined)\n\t\t\t\t.map((c) => c.id as BodygraphCenterId),\n\t\t);\n\t\tconst activeGates = new Set<number>(\n\t\t\t(d.gates ?? []).map((g) => g.gate).filter((n): n is number => n != null),\n\t\t);\n\t\tconst activeChannels = new Set<string>(\n\t\t\t(d.channels ?? []).map((c) => channelKey(c.gateA, c.gateB)),\n\t\t);\n\t\tconst gateTitles = this.buildGateTitles(d.gates ?? []);\n\n\t\treturn html`<div class=\"wrap\"><header class=\"head\"><h2 class=\"title\">Bodygraph</h2>${\n\t\t\t\t\td.type\n\t\t\t\t\t\t? html`<div class=\"type-line\">${d.type}${d.profile ? html`\u00B7 Profile ${d.profile}` : nothing}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header><div class=\"layout\"><svg viewBox=\"${BODYGRAPH_VIEWBOX}\" preserveAspectRatio=\"xMidYMid meet\" role=\"img\" aria-label=\"Human Design bodygraph with nine centers, channels, and activated gates overlaid on a human silhouette\"><title>Human Design bodygraph</title><desc>Nine energy centers in their canonical positions over a human silhouette, each filled with its traditional color when defined and outlined when open, wired by channels between activated gates.</desc>${renderBodygraphSvg({\n\t\t\t\t\t\tdefinedCenters,\n\t\t\t\t\t\tactiveChannels,\n\t\t\t\t\t\tactiveGates,\n\t\t\t\t\t\tgateTitles,\n\t\t\t\t\t})}</svg> ${this.renderSummary(d)}</div></div>`;\n\t}\n\n\tprivate buildGateTitles(gates: GateActivation[]): Map<number, string> {\n\t\tconst titles = new Map<number, string>();\n\t\tfor (const g of gates) {\n\t\t\tif (g.gate == null) continue;\n\t\t\tconst parts: string[] = [`Gate ${g.gate}`];\n\t\t\tif (g.line != null) parts[0] += `.${g.line}`;\n\t\t\tif (g.gateName) parts.push(g.gateName);\n\t\t\tconst planet = g.planet ? capitalize(g.planet) : '';\n\t\t\tconst glyph = planet ? (PLANET_GLYPH[planet] ?? planet) : '';\n\t\t\tif (glyph) parts.push(`${glyph} ${g.side ?? ''}`.trim());\n\t\t\ttitles.set(g.gate, parts.join(' \u00B7 '));\n\t\t}\n\t\treturn titles;\n\t}\n\n\tprivate renderSummary(d: GenerateBodygraphResponse) {\n\t\tconst facts: Array<{ label: string; value?: string }> = [\n\t\t\t{ label: 'Type', value: d.type },\n\t\t\t{ label: 'Strategy', value: d.strategy },\n\t\t\t{ label: 'Authority', value: d.authority },\n\t\t\t{ label: 'Profile', value: d.profile },\n\t\t\t{ label: 'Definition', value: d.definition },\n\t\t];\n\t\tconst ic = d.incarnationCross;\n\t\treturn html`<div class=\"summary\"><div class=\"facts\">${facts.map((f) =>\n\t\t\t\t\tf.value\n\t\t\t\t\t\t? html`<div class=\"fact\"><span>${f.label}</span> <strong>${f.value}</strong></div>`\n\t\t\t\t\t\t: nothing,\n\t\t\t\t)}</div>${\n\t\t\t\tic?.name\n\t\t\t\t\t? html`<p class=\"cross\">${ic.name} ${\n\t\t\t\t\t\t\tic.gates?.length\n\t\t\t\t\t\t\t\t? html`<span class=\"gates\">(${ic.gates.join(', ')})</span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\td.signature || d.notSelf\n\t\t\t\t\t? html`<div class=\"themes\">${d.signature ? html`<span class=\"pill pill--good\">Signature: ${d.signature}</span>` : nothing} ${d.notSelf ? html`<span class=\"pill pill--shadow\">Not-self: ${d.notSelf}</span>` : nothing}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}<div class=\"legend\"><span class=\"legend-caption\">Center colors when defined. Open centers are outlined.</span> <span><span class=\"swatch bg-gold defined\"></span>Head, G</span> <span><span class=\"swatch bg-green defined\"></span>Ajna</span> <span><span class=\"swatch bg-brown defined\"></span>Throat, Spleen, Solar Plexus, Root</span> <span><span class=\"swatch bg-red defined\"></span>Heart, Sacral</span> <span><span class=\"swatch\"></span>Open center</span></div></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-bodygraph': RoxyBodygraph;\n\t}\n}\n", "/**\n * Symbol constants used across components. Single source of truth so chart\n * wheels, card headers, hexagram displays, and panchang tables stay visually\n * consistent.\n */\n\nexport const PLANET_GLYPH: Record<string, string> = {\n\tSun: '\u2609',\n\tMoon: '\u263D',\n\tMercury: '\u263F',\n\tVenus: '\u2640',\n\tEarth: '\u2641',\n\tMars: '\u2642',\n\tJupiter: '\u2643',\n\tSaturn: '\u2644',\n\tUranus: '\u2645',\n\tNeptune: '\u2646',\n\tPluto: '\u2647',\n\tRahu: '\u260A',\n\tKetu: '\u260B',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n\tNorthNode: '\u260A',\n\tSouthNode: '\u260B',\n\t'North node': '\u260A',\n\t'South node': '\u260B',\n\tChiron: '\u26B7',\n\tLilith: '\u26B8',\n\t'Black moon lilith': '\u26B8',\n};\n\nexport const PLANET_ABBR: Record<string, string> = {\n\tSun: 'Su',\n\tMoon: 'Mo',\n\tMercury: 'Me',\n\tVenus: 'Ve',\n\tMars: 'Ma',\n\tJupiter: 'Ju',\n\tSaturn: 'Sa',\n\tUranus: 'Ur',\n\tNeptune: 'Ne',\n\tPluto: 'Pl',\n\tRahu: 'Ra',\n\tKetu: 'Ke',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n};\n\nexport const SIGN_GLYPH: Record<string, string> = {\n\tAries: '\u2648',\n\tTaurus: '\u2649',\n\tGemini: '\u264A',\n\tCancer: '\u264B',\n\tLeo: '\u264C',\n\tVirgo: '\u264D',\n\tLibra: '\u264E',\n\tScorpio: '\u264F',\n\tSagittarius: '\u2650',\n\tCapricorn: '\u2651',\n\tAquarius: '\u2652',\n\tPisces: '\u2653',\n};\n\nexport const SIGN_ABBR: Record<string, string> = {\n\tAries: 'Ar',\n\tTaurus: 'Ta',\n\tGemini: 'Ge',\n\tCancer: 'Cn',\n\tLeo: 'Le',\n\tVirgo: 'Vi',\n\tLibra: 'Li',\n\tScorpio: 'Sc',\n\tSagittarius: 'Sg',\n\tCapricorn: 'Cp',\n\tAquarius: 'Aq',\n\tPisces: 'Pi',\n};\n\nexport const SIGNS_ORDER = [\n\t'Aries',\n\t'Taurus',\n\t'Gemini',\n\t'Cancer',\n\t'Leo',\n\t'Virgo',\n\t'Libra',\n\t'Scorpio',\n\t'Sagittarius',\n\t'Capricorn',\n\t'Aquarius',\n\t'Pisces',\n] as const;\n\n/**\n * Lowercase rashi keys in canonical zodiac order. Derived from `SIGNS_ORDER`\n * so the two stay in lockstep. The /vedic-astrology/birth-chart response\n * carries planet buckets keyed by these names.\n */\nexport const RASHI_KEYS = SIGNS_ORDER.map((s) =>\n\ts.toLowerCase(),\n) as readonly Lowercase<(typeof SIGNS_ORDER)[number]>[];\n\n/** Aspect symbols. Used by synastry and natal chart aspect tables. */\nexport const ASPECT_SYMBOL: Record<string, string> = {\n\tconjunction: '\u260C',\n\topposition: '\u260D',\n\ttrine: '\u25B3',\n\tsquare: '\u25A1',\n\tsextile: '\u2731',\n\tquincunx: '\u22BB',\n\tsemisextile: '\u22BC',\n};\n\n/** Trigrams used by I Ching hexagrams. Eight trigrams compose 64 hexagrams. */\nexport const TRIGRAM_GLYPH: Record<string, string> = {\n\theaven: '\u2630',\n\tlake: '\u2631',\n\tfire: '\u2632',\n\tthunder: '\u2633',\n\twind: '\u2634',\n\twater: '\u2635',\n\tmountain: '\u2636',\n\tearth: '\u2637',\n\tHeaven: '\u2630',\n\tLake: '\u2631',\n\tFire: '\u2632',\n\tThunder: '\u2633',\n\tWind: '\u2634',\n\tWater: '\u2635',\n\tMountain: '\u2636',\n\tEarth: '\u2637',\n};\n\n/** Moon phase emoji set. Used by moon phase card. */\nexport const MOON_PHASE_EMOJI: Record<string, string> = {\n\t'new moon': '\uD83C\uDF11',\n\t'waxing crescent': '\uD83C\uDF12',\n\t'first quarter': '\uD83C\uDF13',\n\t'waxing gibbous': '\uD83C\uDF14',\n\t'full moon': '\uD83C\uDF15',\n\t'waning gibbous': '\uD83C\uDF16',\n\t'last quarter': '\uD83C\uDF17',\n\t'waning crescent': '\uD83C\uDF18',\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`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;\n", "import type { TemplateResult } from 'lit';\nimport { nothing, svg } from 'lit';\n\n/**\n * Fixed geometry and renderer for the Human Design bodygraph. The diagram is a\n * standard, invariant layout: nine centers in canonical positions and shapes,\n * wired by 36 channels that each join two gates, with the 64 gates at fixed\n * points on the center edges, all overlaid on a front-facing human silhouette so\n * each center lands on the body part it governs. Only which centers are defined\n * and which channels and gates are active changes per chart, so this module holds\n * the geometry and the {@link RoxyBodygraph} component supplies the live state\n * from the /human-design/bodygraph response.\n *\n * @remarks Every point is authored in one normalized 0 to 100 canonical grid (x\n * left to right, y top to bottom) taken from the reference Jovian Archive\n * bodygraph, then scaled into {@link BODYGRAPH_VIEWBOX} by a single transform so\n * the chart and the body share one coordinate space and scale together. The grid\n * is sized so the nine centers fill the figure exactly as in the canonical\n * \"nine centers on the human body\" diagram: the Head center at the crown, Ajna at\n * the forehead, Throat at the neck, G at the chest, Heart at the right chest,\n * Spleen on the left torso, Solar Plexus on the right torso (its mirror), Sacral\n * at the lower abdomen, and Root at the pelvis. Two structural truths the layout\n * preserves: the Spleen (left) and the Solar Plexus (right) are mirror images at\n * the Sacral height, so the chart is narrow at the top and wide at the bottom;\n * and the Heart sits low and to the right of the G center, above the Solar\n * Plexus. The reference chart spreads the two side centers to the page edge for\n * channel clarity; here they are shifted inward by a fixed amount so they rest on\n * the torso sides inside the figure, with every channel topology preserved. Center shapes follow the canonical orientations (Head triangle\n * up, Ajna triangle down, Throat and Sacral and Root squares, G diamond, Heart\n * triangle pointing right, Spleen triangle pointing right with its base on the\n * far-left edge, Solar Plexus its mirror). The 36 channel gate pairs are the\n * gold-standard set used by the RoxyAPI Human Design engine.\n */\n\nexport type BodygraphCenterId =\n\t| 'head'\n\t| 'ajna'\n\t| 'throat'\n\t| 'g'\n\t| 'heart'\n\t| 'sacral'\n\t| 'solar-plexus'\n\t| 'spleen'\n\t| 'root';\n\ninterface Point {\n\tx: number;\n\ty: number;\n}\n\n/**\n * One center's drawable geometry: its semantic traditional color, the polygon\n * point list of its canonical shape, and the anchor where its name label sits.\n * Shapes are explicit point lists so the triangle and diamond orientations match\n * the canonical diagram exactly. Label anchors sit in the empty margins outside\n * each shape so they never collide with the gate numbers printed on the edges.\n */\ninterface CenterGeometry {\n\tid: BodygraphCenterId;\n\tlabel: string;\n\tcolor: CenterColor;\n\tpoints: Point[];\n\tlabelAnchor: Point;\n\tlabelAlign: 'start' | 'middle' | 'end';\n}\n\n/**\n * Traditional Human Design center color group. A defined center is filled with\n * this semantic color (constant across light and dark, like chart data colors);\n * an open center is left transparent with a thin theme-aware outline. The four\n * groups mirror the canonical scheme: gold for the identity and inspiration\n * centers (Head, G), green for the mental awareness center (Ajna), red for the\n * life-force motors of will and vitality (Heart, Sacral), brown for the\n * pressure, expression, and remaining awareness centers (Throat, Spleen, Solar\n * Plexus, Root).\n */\nexport type CenterColor = 'gold' | 'green' | 'red' | 'brown';\n\n/**\n * Viewport mapping from the normalized 0 to 100 grid into the SVG viewBox. The\n * chart proper occupies the inner grid; {@link PAD} is the slim margin (in grid\n * units) the body silhouette extends past the centers on every side, so the head\n * rises just above the Head center, the hips sit just below Root, and the\n * shoulders spread just outside Spleen and Solar Plexus. UNIT scales grid units\n * to viewBox units; the chart keeps its natural narrow-top, wide-bottom shape\n * because x and y share one scale.\n */\nconst UNIT = 4;\nconst PAD = 9;\nconst VIEW_W = (100 + 2 * PAD) * UNIT;\nconst VIEW_H = (100 + 2 * PAD) * UNIT;\n\n/** Map a normalized grid point (0 to 100) into viewBox units. */\nfunction g(x: number, y: number): Point {\n\treturn { x: (x + PAD) * UNIT, y: (y + PAD) * UNIT };\n}\n\n/** Chart horizontal center, the axis of symmetry for the body and the side centers. */\nconst AXIS = 50;\n\n/** Reflect a normalized grid x across {@link AXIS}, the vertical axis of symmetry. */\nconst mirrorX = (x: number): number => 2 * AXIS - x;\n\n/**\n * Gate positions authored per center in the normalized 0 to 100 grid, before the\n * mapping through {@link g}. Symmetry is structural, not hand-typed: every center\n * on the central column is authored balanced about {@link AXIS}; the Spleen is\n * authored once on the left torso and the Solar Plexus is derived as its exact\n * mirror in {@link buildGatePoints}, so the two side centers can never drift out\n * of alignment. The Heart is the one center the canonical bodygraph places off\n * the axis (low and to the right of the G center, with no left counterpart).\n * Within each center the gates follow the canonical reading order so the numbers\n * print where a printed chart shows them.\n *\n * @remarks Solar Plexus is intentionally empty here and filled by reflecting\n * Spleen; {@link SPLEEN_TO_SOLAR_PLEXUS} pairs each Spleen gate with the Solar\n * Plexus gate at its mirror position (base-top to base-top, apex to apex).\n */\nconst GATES_BY_CENTER: Record<\n\tBodygraphCenterId,\n\tRecord<number, [number, number]>\n> = {\n\thead: { 64: [45.5, 11.2], 61: [50, 11.2], 63: [54.5, 11.2] },\n\tajna: {\n\t\t47: [45.3, 18.0],\n\t\t24: [50, 18.0],\n\t\t4: [54.7, 18.0],\n\t\t17: [45.6, 20.6],\n\t\t11: [54.4, 20.6],\n\t\t43: [50, 25.3],\n\t},\n\tthroat: {\n\t\t62: [45.5, 32.3],\n\t\t23: [50, 32.3],\n\t\t56: [54.5, 32.3],\n\t\t16: [42, 34.6],\n\t\t35: [58, 34.6],\n\t\t12: [58, 37.6],\n\t\t20: [42, 40.6],\n\t\t45: [58, 40.6],\n\t\t31: [46, 42.4],\n\t\t8: [50, 42.4],\n\t\t33: [54, 42.4],\n\t},\n\tg: {\n\t\t1: [50, 47.5],\n\t\t7: [45.6, 50.3],\n\t\t13: [54.4, 50.3],\n\t\t10: [40, 53.3],\n\t\t25: [60, 53.3],\n\t\t15: [45.6, 56.6],\n\t\t46: [54.4, 56.6],\n\t\t2: [50, 59.0],\n\t},\n\theart: {\n\t\t21: [62.5, 58.5],\n\t\t51: [62.5, 61.3],\n\t\t26: [62.5, 64.1],\n\t\t40: [73, 61.3],\n\t},\n\tspleen: {\n\t\t48: [20, 70.6],\n\t\t57: [24, 72.3],\n\t\t44: [28, 74.0],\n\t\t50: [32, 75.6],\n\t\t32: [28, 77.2],\n\t\t28: [24, 78.9],\n\t\t18: [20, 80.6],\n\t},\n\tsacral: {\n\t\t5: [45.5, 72.5],\n\t\t14: [50, 72.5],\n\t\t29: [54.5, 72.5],\n\t\t34: [42, 75.6],\n\t\t27: [42, 79.0],\n\t\t59: [58, 79.0],\n\t\t42: [45.5, 81.4],\n\t\t3: [50, 81.4],\n\t\t9: [54.5, 81.4],\n\t},\n\t'solar-plexus': {},\n\troot: {\n\t\t53: [45.5, 89.9],\n\t\t60: [50, 89.9],\n\t\t52: [54.5, 89.9],\n\t\t54: [42, 92.7],\n\t\t19: [58, 92.7],\n\t\t38: [42, 95.3],\n\t\t39: [58, 95.3],\n\t\t58: [42, 98.0],\n\t\t41: [58, 98.0],\n\t},\n};\n\n/** Spleen gate to the Solar Plexus gate at its mirror position (base-top, ..., apex, ..., base-bottom). */\nconst SPLEEN_TO_SOLAR_PLEXUS: Record<number, number> = {\n\t48: 36,\n\t57: 22,\n\t44: 37,\n\t50: 6,\n\t32: 49,\n\t28: 55,\n\t18: 30,\n};\n\n/**\n * Assemble the viewBox-space gate anchors and the gate to center index from\n * {@link GATES_BY_CENTER}, filling the Solar Plexus by reflecting the Spleen so\n * the two side centers are guaranteed mirror images. Each gate sits on the\n * canonical edge of its center where its channels connect, so channel lines join\n * gate to gate cleanly and the activated numbers print in their traditional spots.\n */\nfunction buildGatePoints(): {\n\tpoints: Record<number, Point>;\n\tcenterOf: Record<number, BodygraphCenterId>;\n} {\n\tconst points: Record<number, Point> = {};\n\tconst centerOf: Record<number, BodygraphCenterId> = {};\n\tfor (const [spleenGate, [x, y]] of Object.entries(GATES_BY_CENTER.spleen)) {\n\t\tGATES_BY_CENTER['solar-plexus'][\n\t\t\tSPLEEN_TO_SOLAR_PLEXUS[Number(spleenGate)]\n\t\t] = [mirrorX(x), y];\n\t}\n\tfor (const [center, gates] of Object.entries(GATES_BY_CENTER) as Array<\n\t\t[BodygraphCenterId, Record<number, [number, number]>]\n\t>) {\n\t\tfor (const [gate, [x, y]] of Object.entries(gates)) {\n\t\t\tpoints[Number(gate)] = g(x, y);\n\t\t\tcenterOf[Number(gate)] = center;\n\t\t}\n\t}\n\treturn { points, centerOf };\n}\n\n/**\n * The viewBox-space gate anchors ({@link GATE_POINTS}) and the gate to center\n * index ({@link GATE_CENTER}). Exported so the geometry tests can assert the\n * layout invariants (side-center mirror symmetry, central-column balance, gates\n * inside their centers) without rendering.\n */\nexport const { points: GATE_POINTS, centerOf: GATE_CENTER } = buildGatePoints();\n\n/** Horizontal axis of symmetry in viewBox units, the reflection axis for geometry tests. */\nexport const CHART_AXIS_X = VIEW_W / 2;\n\n/** Build a polygon from normalized grid corner pairs, mapping each through {@link g}. */\nfunction shape(corners: ReadonlyArray<readonly [number, number]>): Point[] {\n\treturn corners.map(([x, y]) => g(x, y));\n}\n\n/** A square center, centered on {@link AXIS}, spanning the given half-width and y range. */\nfunction squareShape(halfWidth: number, top: number, bottom: number): Point[] {\n\treturn shape([\n\t\t[AXIS - halfWidth, top],\n\t\t[AXIS + halfWidth, top],\n\t\t[AXIS + halfWidth, bottom],\n\t\t[AXIS - halfWidth, bottom],\n\t]);\n}\n\n/** The Spleen triangle (base on the far-left edge, apex pointing right toward center). */\nconst SPLEEN_SHAPE: ReadonlyArray<readonly [number, number]> = [\n\t[18.4, 68.0],\n\t[18.4, 81.8],\n\t[34.7, 74.9],\n];\n\n/**\n * Center shapes in canonical orientation and color, labels anchored in the\n * margins. Central-column centers are built centered on {@link AXIS}; the Solar\n * Plexus shape is the Spleen reflected across the axis, so the side centers stay\n * exact mirrors. The Heart is the deliberate off-axis exception.\n */\nexport const CENTER_GEOMETRY: readonly CenterGeometry[] = [\n\t{\n\t\tid: 'head',\n\t\tlabel: 'Head',\n\t\tcolor: 'gold',\n\t\tpoints: shape([\n\t\t\t[40.0, 14.3],\n\t\t\t[60.0, 14.3],\n\t\t\t[50.0, 6.0],\n\t\t]),\n\t\tlabelAnchor: g(63, 9),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'ajna',\n\t\tlabel: 'Ajna',\n\t\tcolor: 'green',\n\t\tpoints: shape([\n\t\t\t[40.0, 15.6],\n\t\t\t[60.0, 15.6],\n\t\t\t[50.0, 27.6],\n\t\t]),\n\t\tlabelAnchor: g(62, 21),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'throat',\n\t\tlabel: 'Throat',\n\t\tcolor: 'brown',\n\t\tpoints: squareShape(9.5, 30.4, 43.6),\n\t\tlabelAnchor: g(83, 34),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'g',\n\t\tlabel: 'G',\n\t\tcolor: 'gold',\n\t\tpoints: shape([\n\t\t\t[50.0, 45.0],\n\t\t\t[60.7, 53.3],\n\t\t\t[50.0, 61.6],\n\t\t\t[39.3, 53.3],\n\t\t]),\n\t\tlabelAnchor: g(13, 51),\n\t\tlabelAlign: 'end',\n\t},\n\t{\n\t\tid: 'heart',\n\t\tlabel: 'Heart',\n\t\tcolor: 'red',\n\t\tpoints: shape([\n\t\t\t[61.5, 57.0],\n\t\t\t[76.5, 61.3],\n\t\t\t[61.5, 65.6],\n\t\t]),\n\t\tlabelAnchor: g(85, 56),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'spleen',\n\t\tlabel: 'Spleen',\n\t\tcolor: 'brown',\n\t\tpoints: shape(SPLEEN_SHAPE),\n\t\tlabelAnchor: g(13, 70),\n\t\tlabelAlign: 'end',\n\t},\n\t{\n\t\tid: 'sacral',\n\t\tlabel: 'Sacral',\n\t\tcolor: 'red',\n\t\tpoints: squareShape(9.5, 70.6, 83.6),\n\t\t// Lower-right, below the Solar Plexus: a left-side leader would clip the\n\t\t// Spleen, which sits at the same height as the Sacral.\n\t\tlabelAnchor: g(85, 88),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'solar-plexus',\n\t\tlabel: 'Solar Plexus',\n\t\tcolor: 'brown',\n\t\tpoints: shape(\n\t\t\tSPLEEN_SHAPE.map(([x, y]) => [mirrorX(x), y] as [number, number]),\n\t\t),\n\t\tlabelAnchor: g(87, 73),\n\t\tlabelAlign: 'start',\n\t},\n\t{\n\t\tid: 'root',\n\t\tlabel: 'Root',\n\t\tcolor: 'brown',\n\t\tpoints: squareShape(9.5, 87.9, 99.9),\n\t\tlabelAnchor: g(50, 103),\n\t\tlabelAlign: 'middle',\n\t},\n];\n\n/**\n * The 36 channels as ordered gate pairs. This is the canonical Human Design\n * channel set; a channel is active only when both of its gates are activated,\n * which the live response reports in its `channels` array. The static list lets\n * the renderer draw every channel as a hanging (inactive) line and overlay the\n * active ones, so an open bodygraph still shows its full wiring skeleton.\n */\nexport const CHANNEL_PAIRS: ReadonlyArray<readonly [number, number]> = [\n\t[64, 47],\n\t[61, 24],\n\t[63, 4],\n\t[17, 62],\n\t[11, 56],\n\t[43, 23],\n\t[16, 48],\n\t[20, 34],\n\t[20, 10],\n\t[7, 31],\n\t[1, 8],\n\t[13, 33],\n\t[21, 45],\n\t[12, 22],\n\t[35, 36],\n\t[57, 20],\n\t[15, 5],\n\t[2, 14],\n\t[46, 29],\n\t[34, 10],\n\t[10, 57],\n\t[25, 51],\n\t[27, 50],\n\t[57, 34],\n\t[26, 44],\n\t[18, 58],\n\t[28, 38],\n\t[32, 54],\n\t[3, 60],\n\t[9, 52],\n\t[42, 53],\n\t[59, 6],\n\t[19, 49],\n\t[39, 55],\n\t[41, 30],\n\t[37, 40],\n];\n\n/**\n * Front-facing standing figure behind the chart, mirror-symmetric about\n * {@link AXIS}. Authored in the same normalized grid as the centers, so it scales\n * with the chart and frames it as in the canonical \"nine centers on the human\n * body\" diagram: a rounded head holding the Head center, a short neck at the\n * Throat, shoulders that slope to arms hanging down the outside of the torso so\n * their span frames the Spleen (left) and Solar Plexus (right), a torso that\n * narrows at the waist below the chest, then hips ending at the pelvis just below\n * the Root center. The outline is one closed loop: crown, temple, jaw, neck,\n * shoulder, down the outer arm to the hand beside the hip, in under the hip to\n * the pelvis hem, mirrored back up the left side. The right half is built from\n * cubic beziers and reflected so the figure is exactly symmetric. Drawn first and\n * behind everything so the centers, wiring, and gate numbers stay legible on top.\n */\nconst BODY_PATH = buildBodyPath();\n\nfunction buildBodyPath(): string {\n\tconst m = mirrorX;\n\t// Right-side outline in grid units (x, y) from the crown down to the\n\t// pelvis-right corner: a start point, then triples of (ctrl1, ctrl2, end). A\n\t// rounded head hugging the Head and Ajna centers, a narrowed neck, shoulders at\n\t// their widest, the outer torso running just past the Spleen and Solar Plexus\n\t// (right edges near x 82 at y 74), then waist and hip to a flat pelvis hem. The\n\t// left side is the mirror and the hem is a straight line, so the figure reads as\n\t// a torso, not a point.\n\tconst r: Array<[number, number]> = [\n\t\t[50, -2], // crown apex (start)\n\t\t[60, -2], // crown round (ctrl)\n\t\t[60.5, 9], // head side (ctrl)\n\t\t[57, 18], // brow, head holds Head + Ajna (end)\n\t\t[56, 21], // cheek (ctrl)\n\t\t[54, 24], // jaw (ctrl)\n\t\t[52, 27], // neck right, narrowed (end)\n\t\t[54, 28], // neck base (ctrl)\n\t\t[64, 30], // trapezius slope (ctrl)\n\t\t[80, 34], // shoulder / deltoid, the widest point (end)\n\t\t[83.5, 40], // upper torso side (ctrl)\n\t\t[84, 56], // outer torso, frames the side centers (ctrl)\n\t\t[83, 74], // torso side past Spleen / Solar Plexus (end)\n\t\t[82, 84], // waist (ctrl)\n\t\t[76, 92], // hip (ctrl)\n\t\t[68, 97], // hip (end)\n\t\t[64, 99], // toward the pelvis (ctrl)\n\t\t[62, 100], // pelvis (ctrl)\n\t\t[60, 100], // pelvis-right corner (end)\n\t];\n\tconst segs: string[] = [`M ${pt(r[0])}`];\n\t// Walk the right side as cubic beziers, three points per segment.\n\tfor (let i = 1; i + 2 < r.length; i += 3) {\n\t\tsegs.push(`C ${pt(r[i])} ${pt(r[i + 1])} ${pt(r[i + 2])}`);\n\t}\n\t// Flat pelvis hem across to the mirrored corner.\n\tsegs.push(`L ${ptm(r[r.length - 1], m)}`);\n\t// Mirror the right walk back up the left side to the crown.\n\tfor (let i = r.length - 3; i >= 1; i -= 3) {\n\t\tsegs.push(`C ${ptm(r[i + 1], m)} ${ptm(r[i], m)} ${ptm(r[i - 1], m)}`);\n\t}\n\tsegs.push('Z');\n\treturn segs.join(' ');\n}\n\nfunction pt([x, y]: [number, number]): string {\n\tconst p = g(x, y);\n\treturn `${p.x} ${p.y}`;\n}\n\nfunction ptm([x, y]: [number, number], m: (x: number) => number): string {\n\tconst p = g(m(x), y);\n\treturn `${p.x} ${p.y}`;\n}\n\nfunction polygonPoints(pts: Point[]): string {\n\treturn pts.map((p) => `${p.x},${p.y}`).join(' ');\n}\n\nfunction pairKey(a: number, b: number): string {\n\treturn a < b ? `${a}-${b}` : `${b}-${a}`;\n}\n\n/** Render the body silhouette behind the chart. */\nfunction renderBody(): TemplateResult {\n\treturn svg`<path class=\"bg-body\" d=${BODY_PATH} />`;\n}\n\n/**\n * Render every channel as a single line joining its two gates, so the wiring\n * always reads as a connected diagram rather than stubs poking out of centers.\n * Each of the 36 channels draws a faint full-length connector; a channel with\n * both gates active is redrawn thick and solid (a defined channel); a channel\n * with only one gate active lights that gate's half toward the midpoint over the\n * connector (a hanging gate). This mirrors how a printed bodygraph colors a full\n * channel only when both gates are active and shows a single hanging gate\n * otherwise, while keeping every connection visible.\n */\nfunction renderChannels(\n\tactiveChannels: Set<string>,\n\tactiveGates: Set<number>,\n): TemplateResult[] {\n\tconst lines: TemplateResult[] = [];\n\tfor (const [a, b] of CHANNEL_PAIRS) {\n\t\tconst pa = GATE_POINTS[a];\n\t\tconst pb = GATE_POINTS[b];\n\t\tif (!pa || !pb) continue;\n\t\tlines.push(\n\t\t\tsvg`<line class=\"bg-channel\" x1=${pa.x} y1=${pa.y} x2=${pb.x} y2=${pb.y} />`,\n\t\t);\n\t\tif (activeChannels.has(pairKey(a, b))) {\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-channel on\" x1=${pa.x} y1=${pa.y} x2=${pb.x} y2=${pb.y} />`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst mid = { x: (pa.x + pb.x) / 2, y: (pa.y + pb.y) / 2 };\n\t\tif (activeGates.has(a)) {\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-half\" x1=${pa.x} y1=${pa.y} x2=${mid.x} y2=${mid.y} />`,\n\t\t\t);\n\t\t}\n\t\tif (activeGates.has(b)) {\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-half\" x1=${pb.x} y1=${pb.y} x2=${mid.x} y2=${mid.y} />`,\n\t\t\t);\n\t\t}\n\t}\n\treturn lines;\n}\n\n/** Closest point to `p` on segment `a`-`b`, clamped to the segment ends. */\nfunction closestPointOnSegment(p: Point, a: Point, b: Point): Point {\n\tconst dx = b.x - a.x;\n\tconst dy = b.y - a.y;\n\tconst len2 = dx * dx + dy * dy;\n\tif (len2 === 0) return a;\n\tconst t = Math.max(\n\t\t0,\n\t\tMath.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2),\n\t);\n\treturn { x: a.x + t * dx, y: a.y + t * dy };\n}\n\n/** Closest point on a closed polygon's perimeter to `p`, where a label leader should land. */\nfunction closestPointOnPolygon(p: Point, poly: readonly Point[]): Point {\n\tlet best = poly[0];\n\tlet bestDist = Number.POSITIVE_INFINITY;\n\tfor (let i = 0; i < poly.length; i++) {\n\t\tconst q = closestPointOnSegment(p, poly[i], poly[(i + 1) % poly.length]);\n\t\tconst d = (q.x - p.x) ** 2 + (q.y - p.y) ** 2;\n\t\tif (d < bestDist) {\n\t\t\tbestDist = d;\n\t\t\tbest = q;\n\t\t}\n\t}\n\treturn best;\n}\n\n/**\n * Render the nine center shapes, filled with their semantic color when defined\n * and outlined when open, each with a margin label tied to its shape by a thin\n * leader line so the Heart and every other center is unambiguous regardless of\n * whether it is defined.\n */\nfunction renderCenters(defined: Set<BodygraphCenterId>): TemplateResult[] {\n\treturn CENTER_GEOMETRY.map((c) => {\n\t\tconst isDefined = defined.has(c.id);\n\t\tconst cls = `bg-center bg-${c.color}${isDefined ? ' defined' : ''}`;\n\t\tconst edge = closestPointOnPolygon(c.labelAnchor, c.points);\n\t\treturn svg`<g>\n\t\t\t<line class=\"bg-leader\" x1=${c.labelAnchor.x} y1=${c.labelAnchor.y} x2=${edge.x} y2=${edge.y} />\n\t\t\t<polygon class=${cls} points=${polygonPoints(c.points)}><title>${c.label}: ${isDefined ? 'defined' : 'open'}</title></polygon>\n\t\t\t<text class=\"bg-center-label\" x=${c.labelAnchor.x} y=${c.labelAnchor.y} text-anchor=${c.labelAlign} dominant-baseline=\"central\">${c.label}</text>\n\t\t</g>`;\n\t});\n}\n\n/**\n * Render the activated gate numbers at their fixed points. Numbers sit on top of\n * the filled centers, so a halo (a wider, background-colored stroke under the\n * fill) keeps them legible against any center color in both themes.\n */\nfunction renderGateNumbers(\n\tactiveGates: Set<number>,\n\ttitles: Map<number, string>,\n): TemplateResult[] {\n\tconst out: TemplateResult[] = [];\n\tfor (const [gate, p] of Object.entries(GATE_POINTS)) {\n\t\tconst num = Number(gate);\n\t\tif (!activeGates.has(num)) continue;\n\t\tconst title = titles.get(num);\n\t\tout.push(\n\t\t\tsvg`<text class=\"bg-gate\" x=${p.x} y=${p.y} text-anchor=\"middle\" dominant-baseline=\"central\">${num}${title ? svg`<title>${title}</title>` : nothing}</text>`,\n\t\t);\n\t}\n\treturn out;\n}\n\nexport interface BodygraphRenderInput {\n\tdefinedCenters: Set<BodygraphCenterId>;\n\tactiveChannels: Set<string>;\n\tactiveGates: Set<number>;\n\tgateTitles: Map<number, string>;\n}\n\n/** Build the lookup key for an active channel from its two gate numbers. */\nexport function channelKey(a: number, b: number): string {\n\treturn pairKey(a, b);\n}\n\nexport const BODYGRAPH_VIEWBOX = `0 0 ${VIEW_W} ${VIEW_H}`;\n\n/**\n * Render the full bodygraph SVG inner content for the given live state. The\n * caller wraps it in an `<svg>` with {@link BODYGRAPH_VIEWBOX} and applies its\n * own theming CSS. Draw order: body silhouette under channels under centers\n * under gate numbers, so the body is the backdrop, the wiring sits behind the\n * shapes, and the numbers stay legible on top.\n */\nexport function renderBodygraphSvg(\n\tinput: BodygraphRenderInput,\n): TemplateResult {\n\treturn svg`\n\t\t${renderBody()}\n\t\t${renderChannels(input.activeChannels, input.activeGates)}\n\t\t${renderCenters(input.definedCenters)}\n\t\t${renderGateNumbers(input.activeGates, input.gateTitles)}\n\t`;\n}\n", "import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/**\n * Marker class on the inline JSON script a server emits inside a component.\n *\n * @example\n * ```html\n * <roxy-natal-chart>\n * <script type=\"application/json\" class=\"roxy-data\">{ ...response... }</script>\n * </roxy-natal-chart>\n * ```\n */\nconst ROXY_DATA_CLASS = 'roxy-data';\n\n/**\n * True when the element is a `<script type=\"application/json\">`. Uses tag name and attribute rather than `instanceof HTMLScriptElement` so the check holds in every DOM implementation, including server-rendered and hydration runtimes where the constructor global may be absent.\n */\nfunction isJsonScript(el: Element): boolean {\n\treturn (\n\t\tel.nodeName === 'SCRIPT' && el.getAttribute('type') === 'application/json'\n\t);\n}\n\n/**\n * Host shape the controller drives: any reactive element that exposes a public `data` slot. The controller only writes `data` when the host left it unset, so the JavaScript property path always wins.\n */\ninterface DataHost extends ReactiveControllerHost, HTMLElement {\n\tdata?: unknown;\n}\n\n/**\n * Reactive controller that lets a component hydrate its `data` from embedded markup when no `data` property was assigned in JavaScript.\n *\n * @remarks\n * The server-side and cached-render model: a backend renders the RoxyAPI response into a direct-child `<script type=\"application/json\" class=\"roxy-data\">` element, ships static HTML, and never runs per-element JavaScript to assign a property. On connect this controller reads that script, parses it, and feeds the result to the host. The JavaScript property path is untouched and authoritative: if `host.data` already holds a value when the host connects, the controller does nothing and the markup is ignored.\n *\n * Source resolution order on connect, first hit wins:\n *\n * 1. `host.data` already set in JavaScript -> leave it, read nothing.\n * 2. A direct-child `<script type=\"application/json\" class=\"roxy-data\">` -> parse and use. Direct-child only, so a nested component's own script is never read by an ancestor.\n *\n * Fetching from a URL is intentionally unsupported: that would require a browser-visible key and breaks the server-rendered, cached model these consumers rely on.\n *\n * Timing: {@link hostConnected} runs inside the host `connectedCallback`. For an element parsed from server HTML, its direct children are present by the time the custom element upgrades and connects, so the script is readable here. For an element created with `document.createElement` and connected before any child is appended, there is nothing to read and the property path is the only source, which is exactly the existing behavior.\n *\n * Failure is safe: malformed JSON or a missing script leaves `host.data` untouched, so the host renders its normal empty state.\n *\n * Reading the script never mutates it, and only the marked script is touched, so any sibling fallback markup a server nested inside the element (for no-JavaScript, AMP, or crawler rendering) is left in place.\n *\n * @example\n * ```ts\n * import { MarkupDataController } from '../utils/markup-data.js';\n *\n * export class RoxyExample extends LitElement {\n * constructor() {\n * super();\n * new MarkupDataController(this);\n * }\n *\n * @property({ attribute: false })\n * data: ExampleResponse | null = null;\n * }\n * ```\n */\nexport class MarkupDataController<T = unknown> implements ReactiveController {\n\tprivate readonly host: DataHost;\n\n\tconstructor(host: DataHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\thostConnected() {\n\t\t// Property path wins. If the consumer (React, vanilla `.data =`, the\n\t\t// widgets script, an MCP agent) already set data, never look at markup.\n\t\tif (this.host.data != null) return;\n\n\t\tconst parsed = this.read();\n\t\tif (parsed === undefined) return;\n\n\t\tthis.host.data = parsed as T;\n\t\tthis.host.requestUpdate();\n\t}\n\n\t/**\n\t * Resolve the embedded payload. Returns `undefined` when there is nothing valid to read so the caller can leave `host.data` untouched.\n\t */\n\tprivate read(): T | undefined {\n\t\tconst inline = this.findInlineScript();\n\t\treturn inline ? this.parse(inline.textContent) : undefined;\n\t}\n\n\t/**\n\t * Direct-child `<script type=\"application/json\" class=\"roxy-data\">`. Scoped to immediate children so a nested data-driven component never has its script read by an ancestor, and so sibling fallback markup is ignored.\n\t */\n\tprivate findInlineScript(): Element | null {\n\t\tfor (const child of Array.from(this.host.children)) {\n\t\t\tif (isJsonScript(child) && child.classList.contains(ROXY_DATA_CLASS)) {\n\t\t\t\treturn child;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate parse(text: string | null): T | undefined {\n\t\tif (!text?.trim()) return undefined;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as T;\n\t\t} catch {\n\t\t\t// Malformed embedded JSON: fail safe, leave the host empty state in\n\t\t\t// place rather than throwing during connect.\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n", "/**\n * Shared string helpers used across components. Single source of truth so the\n * same formatting rules apply to every key/label/title that surfaces in the\n * shadow tree.\n *\n * - `capitalize`: title-cases the first character, lowercases the rest. Used\n * when matching API-supplied planet/sign names against the glyph maps in\n * `tokens/index.ts`, which use canonical TitleCase keys.\n * - `humanize`: turns an API key (`birth_date`, `birthDate`, `mahadasha-end`)\n * into a label suitable for display (\"Birth date\", \"Mahadasha end\").\n */\n\nexport function capitalize(s: string): string {\n\tif (!s) return '';\n\treturn s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nexport function 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"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["css", "html", "LitElement", "nothing", "customElement", "property", "PLANET_GLYPH", "SIGNS_ORDER", "RASHI_KEYS", "s", "css", "baseStyles", "nothing", "svg", "UNIT", "PAD", "VIEW_W", "VIEW_H", "g", "x", "y", "AXIS", "mirrorX", "GATES_BY_CENTER", "SPLEEN_TO_SOLAR_PLEXUS", "buildGatePoints", "points", "centerOf", "spleenGate", "center", "gates", "gate", "GATE_POINTS", "GATE_CENTER", "CHART_AXIS_X", "shape", "corners", "squareShape", "halfWidth", "top", "bottom", "SPLEEN_SHAPE", "CENTER_GEOMETRY", "CHANNEL_PAIRS", "BODY_PATH", "buildBodyPath", "m", "r", "segs", "pt", "i", "ptm", "p", "polygonPoints", "pts", "pairKey", "a", "b", "renderBody", "renderChannels", "activeChannels", "activeGates", "lines", "pa", "pb", "mid", "closestPointOnSegment", "dx", "dy", "len2", "t", "closestPointOnPolygon", "poly", "best", "bestDist", "q", "d", "renderCenters", "defined", "c", "isDefined", "cls", "edge", "renderGateNumbers", "titles", "out", "num", "title", "channelKey", "BODYGRAPH_VIEWBOX", "renderBodygraphSvg", "input", "ROXY_DATA_CLASS", "isJsonScript", "el", "MarkupDataController", "host", "parsed", "inline", "child", "text", "capitalize", "s", "RoxyBodygraph", "LitElement", "MarkupDataController", "d", "html", "definedCenters", "c", "activeGates", "g", "n", "activeChannels", "channelKey", "gateTitles", "nothing", "BODYGRAPH_VIEWBOX", "renderBodygraphSvg", "gates", "titles", "parts", "planet", "capitalize", "glyph", "PLANET_GLYPH", "facts", "ic", "f", "baseStyles", "css", "__decorateClass", "property", "customElement"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import type { GenerateTimelineResponse } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Cross-domain forecast timeline. Pass `data` from /forecast/timeline. Events
|
|
5
|
+
* are grouped by calendar date down a vertical axis, each colored by its domain
|
|
6
|
+
* and weighted by significance (0-100), showing the event type, body, target,
|
|
7
|
+
* aspect, and description. The visual language matches the dasha timeline: a
|
|
8
|
+
* left rail of dates, a significance bar per event, and a colored marker for
|
|
9
|
+
* the domain.
|
|
10
|
+
*
|
|
11
|
+
* Theming flows through `--roxy-*` custom properties on `:host`.
|
|
12
|
+
*/
|
|
13
|
+
export declare class RoxyForecastTimeline extends LitElement {
|
|
14
|
+
static styles: import("lit").CSSResult[];
|
|
15
|
+
constructor();
|
|
16
|
+
data: GenerateTimelineResponse | null;
|
|
17
|
+
render(): import("lit").TemplateResult<1>;
|
|
18
|
+
/** Group events by their calendar date, preserving response order. */
|
|
19
|
+
private groupByDate;
|
|
20
|
+
private renderDay;
|
|
21
|
+
private renderEvent;
|
|
22
|
+
/**
|
|
23
|
+
* Compact event headline built from the spec fields. Transit aspects read
|
|
24
|
+
* "body aspect target"; ingresses, stations, eclipses, dasha changes, and
|
|
25
|
+
* critical days fall back to "body" plus the qualifier the spec carries for
|
|
26
|
+
* that type (station direction, eclipse kind). The aspect symbol is colored
|
|
27
|
+
* by nature, matching the chart aspect encoding.
|
|
28
|
+
*/
|
|
29
|
+
private renderHeadline;
|
|
30
|
+
/** Type-specific qualifier text from the optional spec fields. */
|
|
31
|
+
private typeQualifier;
|
|
32
|
+
}
|
|
33
|
+
declare global {
|
|
34
|
+
interface HTMLElementTagNameMap {
|
|
35
|
+
'roxy-forecast-timeline': RoxyForecastTimeline;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=forecast-timeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forecast-timeline.d.ts","sourceRoot":"","sources":["../../src/components/forecast-timeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,UAAU,EAAW,MAAM,KAAK,CAAC;AAGrD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AA0BlE;;;;;;;;;GASG;AACH,qBACa,oBAAqB,SAAQ,UAAU;IACnD,MAAM,CAAC,MAAM,4BA0JX;;IAWF,IAAI,EAAE,wBAAwB,GAAG,IAAI,CAAQ;IAE7C,MAAM;IA0CN,sEAAsE;IACtE,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,WAAW;IAkBnB;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAmBtB,kEAAkE;IAClE,OAAO,CAAC,aAAa;CAWrB;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,wBAAwB,EAAE,oBAAoB,CAAC;KAC/C;CACD"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var S=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var g=(t,r,e,a)=>{for(var n=a>1?void 0:a?$(r,e):r,o=t.length-1,i;o>=0;o--)(i=t[o])&&(n=(a?i(r,e,n):i(n))||n);return a&&n&&S(r,e,n),n};import{css as C,html as s,LitElement as N,nothing as p}from"lit";import{customElement as R,property as T}from"lit/decorators.js";var k=["Aries","Taurus","Gemini","Cancer","Leo","Virgo","Libra","Scorpio","Sagittarius","Capricorn","Aquarius","Pisces"],_=k.map(t=>t.toLowerCase()),v={conjunction:"\u260C",opposition:"\u260D",trine:"\u25B3",square:"\u25A1",sextile:"\u2731",quincunx:"\u22BB",semisextile:"\u22BC"};import{css as A}from"lit";var h=A`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;function u(t){if(typeof t!="string"||t.length===0)return"";let r=new Date(/^\d{4}-\d{2}-\d{2}$/.test(t)?`${t}T00:00:00`:t);return Number.isNaN(r.getTime())?t:r.toLocaleDateString(void 0,{month:"short",day:"numeric",year:"numeric"})}function b(t,r=1){return typeof t!="number"||!Number.isFinite(t)?"":t.toFixed(r).replace(/\.?0+$/,"")}var w={conjunction:"aspect-conjunction",sextile:"aspect-sextile",square:"aspect-square",trine:"aspect-trine",opposition:"aspect-opposition"};var L="roxy-data";function E(t){return t.nodeName==="SCRIPT"&&t.getAttribute("type")==="application/json"}var m=class{constructor(r){this.host=r,r.addController(this)}hostConnected(){if(this.host.data!=null)return;let r=this.read();r!==void 0&&(this.host.data=r,this.host.requestUpdate())}read(){let r=this.findInlineScript();return r?this.parse(r.textContent):void 0}findInlineScript(){for(let r of Array.from(this.host.children))if(E(r)&&r.classList.contains(L))return r;return null}parse(r){if(r?.trim())try{return JSON.parse(r)}catch{return}}};function l(t){return t?t.charAt(0).toUpperCase()+t.slice(1).toLowerCase():""}function f(t){return t.replace(/[_-]+/g," ").replace(/([a-z])([A-Z])/g,"$1 $2").replace(/^\w/,r=>r.toUpperCase())}var D={western:"Western",vedic:"Vedic",biorhythm:"Biorhythm"},M=["western","vedic","biorhythm"],c=class extends N{constructor(){super();this.data=null;new m(this)}render(){let e=this.data;if(!e)return s`<div class="roxy-empty" role="status">No forecast data</div>`;let a=e.events??[],n=this.groupByDate(a),o=M.filter(i=>a.some(d=>d.domain===i));return s`<div class="wrap" aria-label="Forecast timeline"><header class="head"><h2 class="title">Forecast timeline</h2>${e.startDate&&e.endDate?s`<div class="range">${u(e.startDate)} - ${u(e.endDate)} · ${e.count??a.length} events</div>`:p}</header>${o.length?s`<div class="legend">${o.map(i=>s`<span><span class="swatch swatch-${i}"></span>${D[i]}</span>`)}</div>`:p} ${n.length?s`<div class="days" role="list">${n.map(([i,d])=>this.renderDay(i,d))}</div>`:s`<p class="roxy-empty" role="status">No events in this window</p>`}</div>`}groupByDate(e){let a=new Map;for(let n of e){let o=n.date??"",i=a.get(o)??[];i.push(n),a.set(o,i)}return[...a.entries()]}renderDay(e,a){return s`<div class="day" role="listitem"><div class="day-date">${u(e)}</div><div class="events">${a.map(n=>this.renderEvent(n))}</div></div>`}renderEvent(e){let a=typeof e.significance=="number"?e.significance:0,n=Math.max(0,Math.min(100,a));return s`<div class="event"><span class="dot dot-${e.domain}" aria-hidden="true"></span><div class="event-body"><div class="event-line">${this.renderHeadline(e)}</div>${e.description?s`<p class="event-desc">${e.description}</p>`:p}<div class="sig" title="Significance ${n} of 100"><span class="sig-track"><span class="sig-fill sig-fill-${e.domain}" style="width:${n}%"></span> </span><span class="sig-val">${n}</span></div></div></div>`}renderHeadline(e){let a=e.body?l(e.body):"",n=e.target?l(e.target):"",o=e.aspect?e.aspect.toLowerCase():"",i=w[o]??"",d=o?v[o]??o:"",y=typeof e.orb=="number"?b(e.orb,1):"",x=this.typeQualifier(e);return o&&n?s`<strong>${a}</strong> <span class="${i}">${d}</span> <strong>${n}</strong>${y?s`<span class="kind">orb ${y}°</span>`:p}`:s`<strong>${a||f(e.type??"")}</strong>${x?s`<span class="kind">${x}</span>`:p}`}typeQualifier(e){return e.type==="sign-ingress"&&e.target?`enters ${l(e.target)}`:e.type==="retrograde-station"&&e.station?e.station:e.type==="eclipse"?[e.kind,"eclipse"].filter(Boolean).join(" "):e.type==="critical-day"?"critical day":e.type==="dasha-change"&&e.target?`dasha ${l(e.target)}`:f(e.type??"")}};c.styles=[h,C`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.range{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);font-variant-numeric:tabular-nums}.legend{gap:var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend .swatch{vertical-align:middle;border-radius:50%;width:10px;height:10px;margin-right:4px;display:inline-block}.swatch-western{background:var(--roxy-accent,#f59e0b)}.swatch-vedic{background:var(--roxy-info,#2563eb)}.swatch-biorhythm{background:var(--roxy-success,#16a34a)}.day{gap:var(--roxy-space-md,1rem);padding-block:var(--roxy-space-sm,.5rem);border-top:1px solid var(--roxy-border,#e4e4e7);grid-template-columns:4.5rem 1fr;display:grid}.day:first-of-type{border-top:none}.day-date{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;padding-top:2px}.events{gap:var(--roxy-space-sm,.5rem);display:grid}.event{gap:var(--roxy-space-sm,.5rem);grid-template-columns:.6rem 1fr;align-items:start;display:grid}.dot{border-radius:50%;width:.6rem;height:.6rem;margin-top:4px}.dot-western{background:var(--roxy-accent,#f59e0b)}.dot-vedic{background:var(--roxy-info,#2563eb)}.dot-biorhythm{background:var(--roxy-success,#16a34a)}.event-body{min-width:0}.event-line{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}.event-line .aspect-trine,.event-line .aspect-sextile{color:var(--roxy-success-fg,#166534)}.event-line .aspect-square,.event-line .aspect-opposition{color:var(--roxy-danger-fg,#991b1b)}.event-line .aspect-conjunction{color:var(--roxy-accent-ink,#b45309)}.event-line .kind{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.04em;margin-left:.35em}.event-desc{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);margin:2px 0 0}.sig{align-items:center;gap:.4rem;margin-top:4px;display:flex}.sig-track{background:var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);flex:1;max-width:8rem;height:4px;overflow:hidden}.sig-fill{border-radius:var(--roxy-radius-full,9999px);height:100%;display:block}.sig-fill-western{background:var(--roxy-accent,#f59e0b)}.sig-fill-vedic{background:var(--roxy-info,#2563eb)}.sig-fill-biorhythm{background:var(--roxy-success,#16a34a)}.sig-val{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums}`],g([T({attribute:!1})],c.prototype,"data",2),c=g([R("roxy-forecast-timeline")],c);export{c as RoxyForecastTimeline};
|
|
2
|
+
//# sourceMappingURL=forecast-timeline.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/components/forecast-timeline.ts", "../../src/tokens/index.ts", "../../src/utils/base-styles.ts", "../../src/utils/format.ts", "../../src/utils/markup-data.ts", "../../src/utils/string.ts"],
|
|
4
|
+
"sourcesContent": ["import { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { ASPECT_SYMBOL } from '../tokens/index.js';\nimport type { GenerateTimelineResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { ASPECT_CLASS, formatDate, formatNumber } from '../utils/format.js';\nimport { MarkupDataController } from '../utils/markup-data.js';\nimport { capitalize, humanize } from '../utils/string.js';\n\ntype ForecastEvent = GenerateTimelineResponse['events'][number];\ntype ForecastDomain = ForecastEvent['domain'];\n\n/**\n * Display label per forecast domain. Keyed by the spec `domain` union so a new\n * domain in the spec surfaces as a typecheck failure here rather than an\n * unlabeled swatch.\n */\nconst DOMAIN_LABEL: Record<ForecastDomain, string> = {\n\twestern: 'Western',\n\tvedic: 'Vedic',\n\tbiorhythm: 'Biorhythm',\n};\n\nconst DOMAIN_ORDER: readonly ForecastDomain[] = [\n\t'western',\n\t'vedic',\n\t'biorhythm',\n];\n\n/**\n * Cross-domain forecast timeline. Pass `data` from /forecast/timeline. Events\n * are grouped by calendar date down a vertical axis, each colored by its domain\n * and weighted by significance (0-100), showing the event type, body, target,\n * aspect, and description. The visual language matches the dasha timeline: a\n * left rail of dates, a significance bar per event, and a colored marker for\n * the domain.\n *\n * Theming flows through `--roxy-*` custom properties on `:host`.\n */\n@customElement('roxy-forecast-timeline')\nexport class RoxyForecastTimeline extends LitElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.range{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);font-variant-numeric:tabular-nums}.legend{gap:var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend .swatch{vertical-align:middle;border-radius:50%;width:10px;height:10px;margin-right:4px;display:inline-block}.swatch-western{background:var(--roxy-accent,#f59e0b)}.swatch-vedic{background:var(--roxy-info,#2563eb)}.swatch-biorhythm{background:var(--roxy-success,#16a34a)}.day{gap:var(--roxy-space-md,1rem);padding-block:var(--roxy-space-sm,.5rem);border-top:1px solid var(--roxy-border,#e4e4e7);grid-template-columns:4.5rem 1fr;display:grid}.day:first-of-type{border-top:none}.day-date{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;padding-top:2px}.events{gap:var(--roxy-space-sm,.5rem);display:grid}.event{gap:var(--roxy-space-sm,.5rem);grid-template-columns:.6rem 1fr;align-items:start;display:grid}.dot{border-radius:50%;width:.6rem;height:.6rem;margin-top:4px}.dot-western{background:var(--roxy-accent,#f59e0b)}.dot-vedic{background:var(--roxy-info,#2563eb)}.dot-biorhythm{background:var(--roxy-success,#16a34a)}.event-body{min-width:0}.event-line{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}.event-line .aspect-trine,.event-line .aspect-sextile{color:var(--roxy-success-fg,#166534)}.event-line .aspect-square,.event-line .aspect-opposition{color:var(--roxy-danger-fg,#991b1b)}.event-line .aspect-conjunction{color:var(--roxy-accent-ink,#b45309)}.event-line .kind{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.04em;margin-left:.35em}.event-desc{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);margin:2px 0 0}.sig{align-items:center;gap:.4rem;margin-top:4px;display:flex}.sig-track{background:var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);flex:1;max-width:8rem;height:4px;overflow:hidden}.sig-fill{border-radius:var(--roxy-radius-full,9999px);height:100%;display:block}.sig-fill-western{background:var(--roxy-accent,#f59e0b)}.sig-fill-vedic{background:var(--roxy-info,#2563eb)}.sig-fill-biorhythm{background:var(--roxy-success,#16a34a)}.sig-val{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums}`,\n\t];\n\n\tconstructor() {\n\t\tsuper();\n\t\t// Enables hydrating `data` from a direct-child\n\t\t// <script type=\"application/json\" class=\"roxy-data\"> for server-rendered\n\t\t// and cached consumers. The JavaScript `data` property still wins.\n\t\tnew MarkupDataController(this);\n\t}\n\n\t@property({ attribute: false })\n\tdata: GenerateTimelineResponse | 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 forecast data</div>`;\n\n\t\tconst events = d.events ?? [];\n\t\tconst grouped = this.groupByDate(events);\n\t\tconst present = DOMAIN_ORDER.filter((dom) =>\n\t\t\tevents.some((e) => e.domain === dom),\n\t\t);\n\n\t\treturn html`<div class=\"wrap\" aria-label=\"Forecast timeline\"><header class=\"head\"><h2 class=\"title\">Forecast timeline</h2>${\n\t\t\t\t\td.startDate && d.endDate\n\t\t\t\t\t\t? html`<div class=\"range\">${formatDate(d.startDate)} - ${formatDate(d.endDate)} \u00B7 ${d.count ?? events.length} events</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${\n\t\t\t\tpresent.length\n\t\t\t\t\t? html`<div class=\"legend\">${present.map(\n\t\t\t\t\t\t\t(dom) =>\n\t\t\t\t\t\t\t\thtml`<span><span class=\"swatch swatch-${dom}\"></span>${DOMAIN_LABEL[dom]}</span>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tgrouped.length\n\t\t\t\t\t? html`<div class=\"days\" role=\"list\">${grouped.map(([date, dayEvents]) => this.renderDay(date, dayEvents))}</div>`\n\t\t\t\t\t: html`<p class=\"roxy-empty\" role=\"status\">No events in this window</p>`\n\t\t\t}</div>`;\n\t}\n\n\t/** Group events by their calendar date, preserving response order. */\n\tprivate groupByDate(\n\t\tevents: ForecastEvent[],\n\t): Array<[string, ForecastEvent[]]> {\n\t\tconst map = new Map<string, ForecastEvent[]>();\n\t\tfor (const e of events) {\n\t\t\tconst key = e.date ?? '';\n\t\t\tconst list = map.get(key) ?? [];\n\t\t\tlist.push(e);\n\t\t\tmap.set(key, list);\n\t\t}\n\t\treturn [...map.entries()];\n\t}\n\n\tprivate renderDay(date: string, events: ForecastEvent[]) {\n\t\treturn html`<div class=\"day\" role=\"listitem\"><div class=\"day-date\">${formatDate(date)}</div><div class=\"events\">${events.map((e) => this.renderEvent(e))}</div></div>`;\n\t}\n\n\tprivate renderEvent(e: ForecastEvent) {\n\t\tconst sig = typeof e.significance === 'number' ? e.significance : 0;\n\t\tconst width = Math.max(0, Math.min(100, sig));\n\t\treturn html`<div class=\"event\"><span class=\"dot dot-${e.domain}\" aria-hidden=\"true\"></span><div class=\"event-body\"><div class=\"event-line\">${this.renderHeadline(e)}</div>${e.description ? html`<p class=\"event-desc\">${e.description}</p>` : nothing}<div class=\"sig\" title=\"Significance ${width} of 100\"><span class=\"sig-track\"><span class=\"sig-fill sig-fill-${e.domain}\" style=\"width:${width}%\"></span> </span><span class=\"sig-val\">${width}</span></div></div></div>`;\n\t}\n\n\t/**\n\t * Compact event headline built from the spec fields. Transit aspects read\n\t * \"body aspect target\"; ingresses, stations, eclipses, dasha changes, and\n\t * critical days fall back to \"body\" plus the qualifier the spec carries for\n\t * that type (station direction, eclipse kind). The aspect symbol is colored\n\t * by nature, matching the chart aspect encoding.\n\t */\n\tprivate renderHeadline(e: ForecastEvent) {\n\t\tconst body = e.body ? capitalize(e.body) : '';\n\t\tconst target = e.target ? capitalize(e.target) : '';\n\t\tconst aspect = e.aspect ? e.aspect.toLowerCase() : '';\n\t\tconst aspectClass = ASPECT_CLASS[aspect] ?? '';\n\t\tconst aspectSym = aspect ? (ASPECT_SYMBOL[aspect] ?? aspect) : '';\n\t\tconst orb = typeof e.orb === 'number' ? formatNumber(e.orb, 1) : '';\n\t\tconst qualifier = this.typeQualifier(e);\n\n\t\tif (aspect && target) {\n\t\t\treturn html`<strong>${body}</strong> <span class=\"${aspectClass}\">${aspectSym}</span> <strong>${target}</strong>${orb ? html`<span class=\"kind\">orb ${orb}\u00B0</span>` : nothing}`;\n\t\t}\n\t\treturn html`<strong>${body || humanize(e.type ?? '')}</strong>${\n\t\t\tqualifier ? html`<span class=\"kind\">${qualifier}</span>` : nothing\n\t\t}`;\n\t}\n\n\t/** Type-specific qualifier text from the optional spec fields. */\n\tprivate typeQualifier(e: ForecastEvent): string {\n\t\tif (e.type === 'sign-ingress' && e.target)\n\t\t\treturn `enters ${capitalize(e.target)}`;\n\t\tif (e.type === 'retrograde-station' && e.station) return e.station;\n\t\tif (e.type === 'eclipse')\n\t\t\treturn [e.kind, 'eclipse'].filter(Boolean).join(' ');\n\t\tif (e.type === 'critical-day') return 'critical day';\n\t\tif (e.type === 'dasha-change' && e.target)\n\t\t\treturn `dasha ${capitalize(e.target)}`;\n\t\treturn humanize(e.type ?? '');\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-forecast-timeline': RoxyForecastTimeline;\n\t}\n}\n", "/**\n * Symbol constants used across components. Single source of truth so chart\n * wheels, card headers, hexagram displays, and panchang tables stay visually\n * consistent.\n */\n\nexport const PLANET_GLYPH: Record<string, string> = {\n\tSun: '\u2609',\n\tMoon: '\u263D',\n\tMercury: '\u263F',\n\tVenus: '\u2640',\n\tEarth: '\u2641',\n\tMars: '\u2642',\n\tJupiter: '\u2643',\n\tSaturn: '\u2644',\n\tUranus: '\u2645',\n\tNeptune: '\u2646',\n\tPluto: '\u2647',\n\tRahu: '\u260A',\n\tKetu: '\u260B',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n\tNorthNode: '\u260A',\n\tSouthNode: '\u260B',\n\t'North node': '\u260A',\n\t'South node': '\u260B',\n\tChiron: '\u26B7',\n\tLilith: '\u26B8',\n\t'Black moon lilith': '\u26B8',\n};\n\nexport const PLANET_ABBR: Record<string, string> = {\n\tSun: 'Su',\n\tMoon: 'Mo',\n\tMercury: 'Me',\n\tVenus: 'Ve',\n\tMars: 'Ma',\n\tJupiter: 'Ju',\n\tSaturn: 'Sa',\n\tUranus: 'Ur',\n\tNeptune: 'Ne',\n\tPluto: 'Pl',\n\tRahu: 'Ra',\n\tKetu: 'Ke',\n\tAscendant: 'Asc',\n\tLagna: 'La',\n};\n\nexport const SIGN_GLYPH: Record<string, string> = {\n\tAries: '\u2648',\n\tTaurus: '\u2649',\n\tGemini: '\u264A',\n\tCancer: '\u264B',\n\tLeo: '\u264C',\n\tVirgo: '\u264D',\n\tLibra: '\u264E',\n\tScorpio: '\u264F',\n\tSagittarius: '\u2650',\n\tCapricorn: '\u2651',\n\tAquarius: '\u2652',\n\tPisces: '\u2653',\n};\n\nexport const SIGN_ABBR: Record<string, string> = {\n\tAries: 'Ar',\n\tTaurus: 'Ta',\n\tGemini: 'Ge',\n\tCancer: 'Cn',\n\tLeo: 'Le',\n\tVirgo: 'Vi',\n\tLibra: 'Li',\n\tScorpio: 'Sc',\n\tSagittarius: 'Sg',\n\tCapricorn: 'Cp',\n\tAquarius: 'Aq',\n\tPisces: 'Pi',\n};\n\nexport const SIGNS_ORDER = [\n\t'Aries',\n\t'Taurus',\n\t'Gemini',\n\t'Cancer',\n\t'Leo',\n\t'Virgo',\n\t'Libra',\n\t'Scorpio',\n\t'Sagittarius',\n\t'Capricorn',\n\t'Aquarius',\n\t'Pisces',\n] as const;\n\n/**\n * Lowercase rashi keys in canonical zodiac order. Derived from `SIGNS_ORDER`\n * so the two stay in lockstep. The /vedic-astrology/birth-chart response\n * carries planet buckets keyed by these names.\n */\nexport const RASHI_KEYS = SIGNS_ORDER.map((s) =>\n\ts.toLowerCase(),\n) as readonly Lowercase<(typeof SIGNS_ORDER)[number]>[];\n\n/** Aspect symbols. Used by synastry and natal chart aspect tables. */\nexport const ASPECT_SYMBOL: Record<string, string> = {\n\tconjunction: '\u260C',\n\topposition: '\u260D',\n\ttrine: '\u25B3',\n\tsquare: '\u25A1',\n\tsextile: '\u2731',\n\tquincunx: '\u22BB',\n\tsemisextile: '\u22BC',\n};\n\n/** Trigrams used by I Ching hexagrams. Eight trigrams compose 64 hexagrams. */\nexport const TRIGRAM_GLYPH: Record<string, string> = {\n\theaven: '\u2630',\n\tlake: '\u2631',\n\tfire: '\u2632',\n\tthunder: '\u2633',\n\twind: '\u2634',\n\twater: '\u2635',\n\tmountain: '\u2636',\n\tearth: '\u2637',\n\tHeaven: '\u2630',\n\tLake: '\u2631',\n\tFire: '\u2632',\n\tThunder: '\u2633',\n\tWind: '\u2634',\n\tWater: '\u2635',\n\tMountain: '\u2636',\n\tEarth: '\u2637',\n};\n\n/** Moon phase emoji set. Used by moon phase card. */\nexport const MOON_PHASE_EMOJI: Record<string, string> = {\n\t'new moon': '\uD83C\uDF11',\n\t'waxing crescent': '\uD83C\uDF12',\n\t'first quarter': '\uD83C\uDF13',\n\t'waxing gibbous': '\uD83C\uDF14',\n\t'full moon': '\uD83C\uDF15',\n\t'waning gibbous': '\uD83C\uDF16',\n\t'last quarter': '\uD83C\uDF17',\n\t'waning crescent': '\uD83C\uDF18',\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`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}*,:before,:after{box-sizing:border-box}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;\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\n/**\n * CSS class name per aspect type. Used by natal and synastry chart aspect\n * lines so the same color encoding (harmonious vs challenging) applies in\n * both wheels. Keys are lowercase canonical names, values are CSS class\n * suffixes the chart components define in their `:host` styles.\n */\nexport const ASPECT_CLASS: Record<string, string> = {\n\tconjunction: 'aspect-conjunction',\n\tsextile: 'aspect-sextile',\n\tsquare: 'aspect-square',\n\ttrine: 'aspect-trine',\n\topposition: 'aspect-opposition',\n};\n\n/**\n * Normalize the `type` field on an aspect entry to a lowercase, hyphen-separated\n * canonical name (`SEMI_SEXTILE` \u2192 `semi-sextile`). Accepts any aspect-shaped\n * object so both natal and synastry inter-aspect entries can share this.\n */\nexport function normalizeAspect(a: { type?: string }): string {\n\treturn (a.type ?? '').toLowerCase().replace(/_/g, '-');\n}\n", "import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/**\n * Marker class on the inline JSON script a server emits inside a component.\n *\n * @example\n * ```html\n * <roxy-natal-chart>\n * <script type=\"application/json\" class=\"roxy-data\">{ ...response... }</script>\n * </roxy-natal-chart>\n * ```\n */\nconst ROXY_DATA_CLASS = 'roxy-data';\n\n/**\n * True when the element is a `<script type=\"application/json\">`. Uses tag name and attribute rather than `instanceof HTMLScriptElement` so the check holds in every DOM implementation, including server-rendered and hydration runtimes where the constructor global may be absent.\n */\nfunction isJsonScript(el: Element): boolean {\n\treturn (\n\t\tel.nodeName === 'SCRIPT' && el.getAttribute('type') === 'application/json'\n\t);\n}\n\n/**\n * Host shape the controller drives: any reactive element that exposes a public `data` slot. The controller only writes `data` when the host left it unset, so the JavaScript property path always wins.\n */\ninterface DataHost extends ReactiveControllerHost, HTMLElement {\n\tdata?: unknown;\n}\n\n/**\n * Reactive controller that lets a component hydrate its `data` from embedded markup when no `data` property was assigned in JavaScript.\n *\n * @remarks\n * The server-side and cached-render model: a backend renders the RoxyAPI response into a direct-child `<script type=\"application/json\" class=\"roxy-data\">` element, ships static HTML, and never runs per-element JavaScript to assign a property. On connect this controller reads that script, parses it, and feeds the result to the host. The JavaScript property path is untouched and authoritative: if `host.data` already holds a value when the host connects, the controller does nothing and the markup is ignored.\n *\n * Source resolution order on connect, first hit wins:\n *\n * 1. `host.data` already set in JavaScript -> leave it, read nothing.\n * 2. A direct-child `<script type=\"application/json\" class=\"roxy-data\">` -> parse and use. Direct-child only, so a nested component's own script is never read by an ancestor.\n *\n * Fetching from a URL is intentionally unsupported: that would require a browser-visible key and breaks the server-rendered, cached model these consumers rely on.\n *\n * Timing: {@link hostConnected} runs inside the host `connectedCallback`. For an element parsed from server HTML, its direct children are present by the time the custom element upgrades and connects, so the script is readable here. For an element created with `document.createElement` and connected before any child is appended, there is nothing to read and the property path is the only source, which is exactly the existing behavior.\n *\n * Failure is safe: malformed JSON or a missing script leaves `host.data` untouched, so the host renders its normal empty state.\n *\n * Reading the script never mutates it, and only the marked script is touched, so any sibling fallback markup a server nested inside the element (for no-JavaScript, AMP, or crawler rendering) is left in place.\n *\n * @example\n * ```ts\n * import { MarkupDataController } from '../utils/markup-data.js';\n *\n * export class RoxyExample extends LitElement {\n * constructor() {\n * super();\n * new MarkupDataController(this);\n * }\n *\n * @property({ attribute: false })\n * data: ExampleResponse | null = null;\n * }\n * ```\n */\nexport class MarkupDataController<T = unknown> implements ReactiveController {\n\tprivate readonly host: DataHost;\n\n\tconstructor(host: DataHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\thostConnected() {\n\t\t// Property path wins. If the consumer (React, vanilla `.data =`, the\n\t\t// widgets script, an MCP agent) already set data, never look at markup.\n\t\tif (this.host.data != null) return;\n\n\t\tconst parsed = this.read();\n\t\tif (parsed === undefined) return;\n\n\t\tthis.host.data = parsed as T;\n\t\tthis.host.requestUpdate();\n\t}\n\n\t/**\n\t * Resolve the embedded payload. Returns `undefined` when there is nothing valid to read so the caller can leave `host.data` untouched.\n\t */\n\tprivate read(): T | undefined {\n\t\tconst inline = this.findInlineScript();\n\t\treturn inline ? this.parse(inline.textContent) : undefined;\n\t}\n\n\t/**\n\t * Direct-child `<script type=\"application/json\" class=\"roxy-data\">`. Scoped to immediate children so a nested data-driven component never has its script read by an ancestor, and so sibling fallback markup is ignored.\n\t */\n\tprivate findInlineScript(): Element | null {\n\t\tfor (const child of Array.from(this.host.children)) {\n\t\t\tif (isJsonScript(child) && child.classList.contains(ROXY_DATA_CLASS)) {\n\t\t\t\treturn child;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate parse(text: string | null): T | undefined {\n\t\tif (!text?.trim()) return undefined;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as T;\n\t\t} catch {\n\t\t\t// Malformed embedded JSON: fail safe, leave the host empty state in\n\t\t\t// place rather than throwing during connect.\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n", "/**\n * Shared string helpers used across components. Single source of truth so the\n * same formatting rules apply to every key/label/title that surfaces in the\n * shadow tree.\n *\n * - `capitalize`: title-cases the first character, lowercases the rest. Used\n * when matching API-supplied planet/sign names against the glyph maps in\n * `tokens/index.ts`, which use canonical TitleCase keys.\n * - `humanize`: turns an API key (`birth_date`, `birthDate`, `mahadasha-end`)\n * into a label suitable for display (\"Birth date\", \"Mahadasha end\").\n */\n\nexport function capitalize(s: string): string {\n\tif (!s) return '';\n\treturn s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nexport function 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"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["css", "html", "LitElement", "nothing", "customElement", "property", "SIGNS_ORDER", "RASHI_KEYS", "s", "ASPECT_SYMBOL", "css", "baseStyles", "formatDate", "input", "d", "formatNumber", "value", "dp", "ASPECT_CLASS", "ROXY_DATA_CLASS", "isJsonScript", "el", "MarkupDataController", "host", "parsed", "inline", "child", "text", "capitalize", "s", "humanize", "c", "DOMAIN_LABEL", "DOMAIN_ORDER", "RoxyForecastTimeline", "LitElement", "MarkupDataController", "d", "html", "events", "grouped", "present", "dom", "e", "formatDate", "nothing", "date", "dayEvents", "map", "key", "list", "sig", "width", "body", "capitalize", "target", "aspect", "aspectClass", "ASPECT_CLASS", "aspectSym", "ASPECT_SYMBOL", "orb", "formatNumber", "qualifier", "humanize", "baseStyles", "css", "__decorateClass", "property", "customElement"]
|
|
7
|
+
}
|