Layer toggles: show/hide Chiron, asteroids, nodes, Lilith, PoF, minor aspects

Frontend-only filtering over the already-computed chart — wheel, tables,
aspect grid/list, report and synastry views all respect the toggles;
state persists in localStorage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-08-30 22:18:33 +10:00
parent 9c284edc9f
commit 438b4c1b47
3 changed files with 94 additions and 11 deletions

View File

@ -136,6 +136,57 @@ $("#saved-charts").addEventListener("change", (e) => {
});
fillSaved();
/* ---------------- layer toggles ---------------- */
const LAYER_GROUPS = {
chiron: ["Chiron"],
asteroids: ["Ceres", "Pallas", "Juno", "Vesta"],
nodes: ["North Node", "South Node"],
lilith: ["Lilith"],
fortune: ["Part of Fortune"],
};
const LAYER_IDS = ["chiron", "asteroids", "nodes", "lilith", "fortune", "minor"];
function layerState() {
const st = {};
LAYER_IDS.forEach((k) => (st[k] = $("#ly-" + k).checked));
return st;
}
function hiddenBodies() {
const st = layerState();
const hidden = new Set();
for (const [k, names] of Object.entries(LAYER_GROUPS)) {
if (!st[k]) names.forEach((n) => hidden.add(n));
}
return hidden;
}
function filterChart(d) {
const hidden = hiddenBodies();
const showMinor = layerState().minor;
return {
...d,
bodies: d.bodies.filter((b) => !hidden.has(b.name)),
aspects: d.aspects.filter((a) => !hidden.has(a.a) && !hidden.has(a.b) && (showMinor || a.major)),
parallels: d.parallels.filter((p) => !hidden.has(p.a) && !hidden.has(p.b)),
interpretation: (d.interpretation || []).map((sec) => ({
...sec,
items: sec.items.filter((it) => ![...hidden].some((n) => it.title.includes(n))),
})),
};
}
(function initLayers() {
let saved = {};
try { saved = JSON.parse(localStorage.getItem("nategodd-layers") || "{}"); } catch {}
LAYER_IDS.forEach((k) => { if (k in saved) $("#ly-" + k).checked = saved[k]; });
LAYER_IDS.forEach((k) =>
$("#ly-" + k).addEventListener("change", () => {
try { localStorage.setItem("nategodd-layers", JSON.stringify(layerState())); } catch {}
if (lastChart) render(lastChart, true);
if (lastSyn && !$("#syn-results").hidden) renderSynastry(lastSyn, false);
})
);
})();
let lastSyn = null;
/* ---------------- tabs ---------------- */
document.querySelectorAll("#tabs button").forEach((b) =>
b.addEventListener("click", () => {
@ -148,22 +199,25 @@ document.querySelectorAll("#tabs button").forEach((b) =>
$("#print-btn").addEventListener("click", () => window.print());
/* ---------------- render ---------------- */
function render(d) {
function render(d, rerender = false) {
$("#results").hidden = false;
const inp = d.input;
const f = filterChart(d);
$("#chart-header").innerHTML =
`<h2>${esc(inp.name) || "Natal Chart"}</h2>
<p>${inp.date}${inp.time ? " · " + inp.time + " local" : " · time unknown (solar chart)"} · ${esc(inp.place)}
· ${inp.lat.toFixed(3)}°, ${inp.lon.toFixed(3)}° · TZ ${esc(String(inp.tz))} (UTC${inp.utc_offset >= 0 ? "+" : ""}${inp.utc_offset})
· ${d.house_system} houses · ${d.is_day_chart ? "Day" : "Night"} chart</p>`;
drawWheel(d);
drawWheel(f);
renderBalances(d);
renderPositions(d);
renderAspects(d);
renderReport(d);
fillSynPartners();
$("#syn-results").hidden = true;
$("#results").scrollIntoView({ behavior: "smooth" });
renderPositions(f);
renderAspects(f);
renderReport(f);
if (!rerender) {
fillSynPartners();
$("#syn-results").hidden = true;
$("#results").scrollIntoView({ behavior: "smooth" });
}
}
function esc(s) { return String(s ?? "").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c])); }
@ -457,7 +511,20 @@ $("#syn-go").addEventListener("click", async () => {
}
});
function renderSynastry(d) {
function renderSynastry(d0, scroll = true) {
lastSyn = d0;
const hidden = hiddenBodies();
const showMinor = layerState().minor;
const d = {
...d0,
chart_a: { ...d0.chart_a, bodies: d0.chart_a.bodies.filter((b) => !hidden.has(b.name)) },
chart_b: { ...d0.chart_b, bodies: d0.chart_b.bodies.filter((b) => !hidden.has(b.name)) },
interaspects: d0.interaspects.filter((x) => !hidden.has(x.a) && !hidden.has(x.b) && (showMinor || x.major)),
interpretation: d0.interpretation.map((sec) => ({
...sec,
items: sec.items.filter((it) => ![...hidden].some((n) => it.title.includes(n))),
})),
};
$("#syn-results").hidden = false;
const sc = d.scores;
const nameA = d.chart_a.input.name || "Chart A";
@ -498,7 +565,7 @@ function renderSynastry(d) {
r += "</section>";
});
$("#syn-report").innerHTML = r;
$("#syn-results").scrollIntoView({ behavior: "smooth" });
if (scroll) $("#syn-results").scrollIntoView({ behavior: "smooth" });
}
$("#syn-print").addEventListener("click", () => window.print());

View File

@ -72,6 +72,16 @@
<button type="button" id="exp-png" title="download wheel as PNG">⬇ PNG</button>
<button type="button" id="exp-print" title="print / save as PDF">🖨 Print</button>
</div>
<div id="layers">
<span class="layers-label">Layers:</span>
<label class="check"><input type="checkbox" checked disabled> core planets</label>
<label class="check"><input type="checkbox" id="ly-chiron" checked> Chiron</label>
<label class="check"><input type="checkbox" id="ly-asteroids" checked> asteroids</label>
<label class="check"><input type="checkbox" id="ly-nodes" checked> nodes</label>
<label class="check"><input type="checkbox" id="ly-lilith" checked> Lilith</label>
<label class="check"><input type="checkbox" id="ly-fortune" checked> Part of Fortune</label>
<label class="check"><input type="checkbox" id="ly-minor" checked> minor aspects</label>
</div>
<nav id="tabs">
<button data-tab="wheel" class="active">Wheel</button>
<button data-tab="positions">Positions</button>

View File

@ -118,6 +118,12 @@ td.dign { color: var(--gold); font-size: 12px; }
#report .item p { margin: 0; color: var(--ink); }
#print-btn { margin-top: 10px; }
#layers { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; margin-bottom: 10px;
padding: 8px 12px; background: #10101c; border: 1px solid var(--line); border-radius: 8px; }
.layers-label { color: var(--dim); font-size: 11px; text-transform: uppercase; letter-spacing: 1px; }
#layers label.check { font-size: 13px; color: var(--ink); gap: 6px; }
#layers input[disabled] + * , #layers label:has(input[disabled]) { opacity: 0.55; }
#toolbar { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
#toolbar button { font-size: 13px; padding: 6px 12px; background: #1a1a2e; }
#toolbar button:hover { background: #262640; }
@ -146,7 +152,7 @@ td.dign { color: var(--gold); font-size: 12px; }
@media print {
body { background: #fff; color: #000; font-size: 12px; }
header, #form-card, #tabs, footer, #print-btn, #toolbar, #syn-picker, #syn-print { display: none !important; }
header, #form-card, #tabs, footer, #print-btn, #toolbar, #layers, #syn-picker, #syn-print { display: none !important; }
#results { border: none; background: none; }
.tab { display: block !important; }
#report .item b, #report h4, h3 { color: #000; }