add iching tarot links

This commit is contained in:
2026-03-07 16:13:58 -08:00
parent ed9b3bb257
commit 84b340d7d1
7 changed files with 199 additions and 9 deletions

View File

@@ -873,9 +873,20 @@
justify-items: start; justify-items: start;
} }
.iching-tarot-text { .iching-tarot-text {
white-space: pre-line; white-space: normal;
line-height: 1.4; line-height: 1.4;
font-size: 12px; font-size: 12px;
display: grid;
gap: 10px;
}
.iching-tarot-group {
display: grid;
gap: 6px;
}
.iching-tarot-group-title {
color: #a1a1aa;
font-size: 12px;
font-weight: 600;
} }
/* ── Kabbalah sections ──────────────────────────────────────────────── */ /* ── Kabbalah sections ──────────────────────────────────────────────── */

View File

@@ -315,6 +315,8 @@
return; return;
} }
clearChildren(elements.ichingDetailTarotEl);
const upperKey = normalizeSearchValue(entry?.upperTrigram); const upperKey = normalizeSearchValue(entry?.upperTrigram);
const lowerKey = normalizeSearchValue(entry?.lowerTrigram); const lowerKey = normalizeSearchValue(entry?.lowerTrigram);
const upperCards = upperKey ? state.tarotByTrigramName[upperKey] || [] : []; const upperCards = upperKey ? state.tarotByTrigramName[upperKey] || [] : [];
@@ -325,15 +327,79 @@
const upperLabel = upperTrigram?.element || entry?.upperTrigram || "--"; const upperLabel = upperTrigram?.element || entry?.upperTrigram || "--";
const lowerLabel = lowerTrigram?.element || entry?.lowerTrigram || "--"; const lowerLabel = lowerTrigram?.element || entry?.lowerTrigram || "--";
const lines = []; function buildTarotTarget(cardName) {
if (upperKey) { const label = String(cardName || "").trim();
lines.push(`Upper (${upperLabel}): ${upperCards.length ? upperCards.join(", ") : "--"}`); if (!label) {
} return null;
if (lowerKey) {
lines.push(`Lower (${lowerLabel}): ${lowerCards.length ? lowerCards.join(", ") : "--"}`);
} }
elements.ichingDetailTarotEl.textContent = lines.length ? lines.join("\n\n") : "--"; const trumpMatch = label.match(/^key\s*(\d{1,2})\s*:/i);
if (trumpMatch) {
return {
label,
detail: { trumpNumber: Number(trumpMatch[1]) }
};
}
return {
label,
detail: { cardName: label }
};
}
function appendTarotGroup(groupLabel, trigramLabel, cards) {
const group = document.createElement("div");
group.className = "iching-tarot-group";
const title = document.createElement("div");
title.className = "iching-tarot-group-title";
title.textContent = `${groupLabel} (${trigramLabel}):`;
group.appendChild(title);
if (!cards.length) {
const empty = document.createElement("div");
empty.className = "planet-text";
empty.textContent = "--";
group.appendChild(empty);
elements.ichingDetailTarotEl.appendChild(group);
return;
}
const buttonRow = document.createElement("div");
buttonRow.className = "alpha-nav-btns";
cards.forEach((cardName) => {
const target = buildTarotTarget(cardName);
if (!target) {
return;
}
const button = document.createElement("button");
button.type = "button";
button.className = "alpha-nav-btn";
button.textContent = `${target.label}`;
button.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
detail: target.detail
}));
});
buttonRow.appendChild(button);
});
group.appendChild(buttonRow);
elements.ichingDetailTarotEl.appendChild(group);
}
if (upperKey) {
appendTarotGroup("Upper", upperLabel, upperCards);
}
if (lowerKey) {
appendTarotGroup("Lower", lowerLabel, lowerCards);
}
if (!elements.ichingDetailTarotEl.childElementCount) {
elements.ichingDetailTarotEl.textContent = "--";
}
} }
function renderCalendarMonths(entry, elements) { function renderCalendarMonths(entry, elements) {

View File

@@ -13,6 +13,7 @@
buildSmallCardRulershipRelation, buildSmallCardRulershipRelation,
buildSmallCardCourtLinkRelations, buildSmallCardCourtLinkRelations,
buildCubeRelationsForCard, buildCubeRelationsForCard,
buildIChingRelationsForCard,
parseMonthDayToken, parseMonthDayToken,
createRelationListItem, createRelationListItem,
findSephirahForMinorCard findSephirahForMinorCard
@@ -131,6 +132,7 @@
const smallCardCourtLinkRelations = buildSmallCardCourtLinkRelations(card, dedupedRelations); const smallCardCourtLinkRelations = buildSmallCardCourtLinkRelations(card, dedupedRelations);
const mergedCourtDateRelations = [...courtDateRelations, ...smallCardCourtLinkRelations]; const mergedCourtDateRelations = [...courtDateRelations, ...smallCardCourtLinkRelations];
const cubeRelations = buildCubeRelationsForCard(card); const cubeRelations = buildCubeRelationsForCard(card);
const iChingRelations = buildIChingRelationsForCard(card);
const monthRelations = (getMonthRefsByCardId().get(card.id) || []).map((month, index) => { const monthRelations = (getMonthRefsByCardId().get(card.id) || []).map((month, index) => {
const dateRange = String(month?.dateRange || "").trim(); const dateRange = String(month?.dateRange || "").trim();
const context = String(month?.context || "").trim(); const context = String(month?.context || "").trim();
@@ -229,6 +231,7 @@
renderStaticRelationGroup(elements.tarotDetailCourtDateEl, elements.tarotMetaCourtDateCardEl, mergedCourtDateRelations); renderStaticRelationGroup(elements.tarotDetailCourtDateEl, elements.tarotMetaCourtDateCardEl, mergedCourtDateRelations);
renderStaticRelationGroup(elements.tarotDetailHebrewEl, elements.tarotMetaHebrewCardEl, hebrewRelations); renderStaticRelationGroup(elements.tarotDetailHebrewEl, elements.tarotMetaHebrewCardEl, hebrewRelations);
renderStaticRelationGroup(elements.tarotDetailCubeEl, elements.tarotMetaCubeCardEl, cubeRelations); renderStaticRelationGroup(elements.tarotDetailCubeEl, elements.tarotMetaCubeCardEl, cubeRelations);
renderStaticRelationGroup(elements.tarotDetailIChingEl, elements.tarotMetaIChingCardEl, iChingRelations);
renderStaticRelationGroup(elements.tarotDetailCalendarEl, elements.tarotMetaCalendarCardEl, mergedMonthRelations); renderStaticRelationGroup(elements.tarotDetailCalendarEl, elements.tarotMetaCalendarCardEl, mergedMonthRelations);
const kabPathEl = elements.tarotKabPathEl; const kabPathEl = elements.tarotKabPathEl;

View File

@@ -190,6 +190,17 @@
label: `Open ${d.name || monthId} in Calendar` label: `Open ${d.name || monthId} in Calendar`
}; };
} }
if (t === "ichingHexagram") {
const hexagramNumber = Number(d.hexagramNumber || relation?.id);
if (!Number.isFinite(hexagramNumber)) {
return null;
}
return {
event: "nav:iching",
detail: { hexagramNumber },
label: `Open Hexagram ${hexagramNumber} in I Ching`
};
}
if (t === "cubeFace") { if (t === "cubeFace") {
const wallId = d.wallId || relation?.id; const wallId = d.wallId || relation?.id;
if (!wallId) { if (!wallId) {

View File

@@ -102,6 +102,8 @@
return String(value || "") return String(value || "")
.trim() .trim()
.toLowerCase() .toLowerCase()
.replace(/^key\s+\d+\s*:\s*/g, "")
.replace(/\b(pentacles?|coins?)\b/g, "disks")
.replace(/\s+/g, " "); .replace(/\s+/g, " ");
} }
@@ -114,6 +116,14 @@
} }
function resolveTarotTrumpNumber(cardName) { function resolveTarotTrumpNumber(cardName) {
const rawKey = String(cardName || "")
.trim()
.toLowerCase();
const keyMatch = rawKey.match(/\bkey\s*(\d{1,2})\b/);
if (keyMatch) {
return Number(keyMatch[1]);
}
const key = normalizeTarotName(cardName); const key = normalizeTarotName(cardName);
if (!key) { if (!key) {
return null; return null;
@@ -724,11 +734,86 @@
]; ];
} }
function buildIChingRelationsForCard(card, referenceData) {
const iChing = referenceData?.iChing;
const hexagrams = Array.isArray(iChing?.hexagrams) ? iChing.hexagrams : [];
const trigrams = Array.isArray(iChing?.trigrams) ? iChing.trigrams : [];
const correspondences = Array.isArray(iChing?.correspondences?.tarotToTrigram)
? iChing.correspondences.tarotToTrigram
: [];
if (!card || !hexagrams.length || !correspondences.length) {
return [];
}
const trigramByKey = new Map(
trigrams
.map((trigram) => [normalizeRelationId(trigram?.name), trigram])
.filter(([key]) => Boolean(key))
);
const matchedTrigramKeys = [...new Set(
correspondences
.filter((row) => cardMatchesTarotAssociation(card, row?.tarot))
.map((row) => normalizeRelationId(row?.trigram))
.filter(Boolean)
)];
if (!matchedTrigramKeys.length) {
return [];
}
const relations = [];
hexagrams.forEach((hexagram) => {
const positionsByTrigram = new Map();
const upperKey = normalizeRelationId(hexagram?.upperTrigram);
const lowerKey = normalizeRelationId(hexagram?.lowerTrigram);
if (matchedTrigramKeys.includes(upperKey)) {
positionsByTrigram.set(upperKey, ["upper"]);
}
if (matchedTrigramKeys.includes(lowerKey)) {
const existing = positionsByTrigram.get(lowerKey) || [];
positionsByTrigram.set(lowerKey, [...existing, "lower"]);
}
positionsByTrigram.forEach((positions, trigramKey) => {
const trigram = trigramByKey.get(trigramKey) || null;
const sideLabel = positions.length === 2
? "Upper + Lower"
: positions[0] === "upper"
? "Upper"
: "Lower";
const trigramLabel = trigram?.element || trigram?.name || hexagram?.upperTrigram || hexagram?.lowerTrigram || "Trigram";
relations.push({
type: "ichingHexagram",
id: String(hexagram?.number || ""),
label: `Hexagram ${hexagram.number}: ${hexagram.name || "--"} · ${sideLabel} ${trigramLabel}`,
data: {
hexagramNumber: Number(hexagram?.number),
name: hexagram?.name || "",
upperTrigram: hexagram?.upperTrigram || "",
lowerTrigram: hexagram?.lowerTrigram || "",
trigramName: trigram?.name || "",
trigramElement: trigram?.element || "",
positions: positions.join(",")
},
__key: `ichingHexagram|${hexagram?.number}|${trigramKey}|${positions.join(",")}`
});
});
});
return relations.sort((left, right) => Number(left?.data?.hexagramNumber || 999) - Number(right?.data?.hexagramNumber || 999));
}
window.TarotRelationsUi = { window.TarotRelationsUi = {
buildCourtCardByDecanId, buildCourtCardByDecanId,
buildSmallCardCourtLinkRelations, buildSmallCardCourtLinkRelations,
buildMonthReferencesByCard, buildMonthReferencesByCard,
buildCubeRelationsForCard, buildCubeRelationsForCard,
buildIChingRelationsForCard,
parseMonthDayToken parseMonthDayToken
}; };
})(); })();

View File

@@ -236,6 +236,7 @@
tarotMetaCourtDateCardEl: document.getElementById("tarot-meta-courtdate-card"), tarotMetaCourtDateCardEl: document.getElementById("tarot-meta-courtdate-card"),
tarotMetaHebrewCardEl: document.getElementById("tarot-meta-hebrew-card"), tarotMetaHebrewCardEl: document.getElementById("tarot-meta-hebrew-card"),
tarotMetaCubeCardEl: document.getElementById("tarot-meta-cube-card"), tarotMetaCubeCardEl: document.getElementById("tarot-meta-cube-card"),
tarotMetaIChingCardEl: document.getElementById("tarot-meta-iching-card"),
tarotMetaCalendarCardEl: document.getElementById("tarot-meta-calendar-card"), tarotMetaCalendarCardEl: document.getElementById("tarot-meta-calendar-card"),
tarotDetailPlanetEl: document.getElementById("tarot-detail-planet"), tarotDetailPlanetEl: document.getElementById("tarot-detail-planet"),
tarotDetailElementEl: document.getElementById("tarot-detail-element"), tarotDetailElementEl: document.getElementById("tarot-detail-element"),
@@ -244,6 +245,7 @@
tarotDetailCourtDateEl: document.getElementById("tarot-detail-courtdate"), tarotDetailCourtDateEl: document.getElementById("tarot-detail-courtdate"),
tarotDetailHebrewEl: document.getElementById("tarot-detail-hebrew"), tarotDetailHebrewEl: document.getElementById("tarot-detail-hebrew"),
tarotDetailCubeEl: document.getElementById("tarot-detail-cube"), tarotDetailCubeEl: document.getElementById("tarot-detail-cube"),
tarotDetailIChingEl: document.getElementById("tarot-detail-iching"),
tarotDetailCalendarEl: document.getElementById("tarot-detail-calendar"), tarotDetailCalendarEl: document.getElementById("tarot-detail-calendar"),
tarotKabPathEl: document.getElementById("tarot-kab-path"), tarotKabPathEl: document.getElementById("tarot-kab-path"),
tarotHouseOfCardsEl: document.getElementById("tarot-house-of-cards") tarotHouseOfCardsEl: document.getElementById("tarot-house-of-cards")
@@ -303,6 +305,7 @@
buildSmallCardRulershipRelation, buildSmallCardRulershipRelation,
buildSmallCardCourtLinkRelations, buildSmallCardCourtLinkRelations,
buildCubeRelationsForCard, buildCubeRelationsForCard,
buildIChingRelationsForCard,
parseMonthDayToken, parseMonthDayToken,
createRelationListItem, createRelationListItem,
findSephirahForMinorCard findSephirahForMinorCard
@@ -513,6 +516,13 @@
return tarotRelationsUi.buildCubeRelationsForCard(card, state.magickDataset); return tarotRelationsUi.buildCubeRelationsForCard(card, state.magickDataset);
} }
function buildIChingRelationsForCard(card) {
if (typeof tarotRelationsUi.buildIChingRelationsForCard !== "function") {
return [];
}
return tarotRelationsUi.buildIChingRelationsForCard(card, state.referenceData);
}
// Returns nav dispatch config for relations that have a corresponding section, // Returns nav dispatch config for relations that have a corresponding section,
// null for informational-only relations. // null for informational-only relations.
function getRelationNavTarget(relation) { function getRelationNavTarget(relation) {

View File

@@ -244,6 +244,10 @@
<strong>Cube of Space</strong> <strong>Cube of Space</strong>
<ul id="tarot-detail-cube" class="tarot-relations"></ul> <ul id="tarot-detail-cube" class="tarot-relations"></ul>
</div> </div>
<div id="tarot-meta-iching-card" class="tarot-meta-card" hidden>
<strong>I Ching</strong>
<ul id="tarot-detail-iching" class="tarot-relations"></ul>
</div>
<div id="tarot-meta-calendar-card" class="tarot-meta-card" hidden> <div id="tarot-meta-calendar-card" class="tarot-meta-card" hidden>
<strong>Calendar Months</strong> <strong>Calendar Months</strong>
<ul id="tarot-detail-calendar" class="tarot-relations"></ul> <ul id="tarot-detail-calendar" class="tarot-relations"></ul>
@@ -392,7 +396,7 @@
</div> </div>
<div class="planet-meta-card"> <div class="planet-meta-card">
<strong>Tarot Correspondences</strong> <strong>Tarot Correspondences</strong>
<p id="iching-detail-tarot" class="planet-text iching-tarot-text">--</p> <div id="iching-detail-tarot" class="planet-text iching-tarot-text">--</div>
</div> </div>
<div class="planet-meta-card"> <div class="planet-meta-card">
<strong>Calendar Months</strong> <strong>Calendar Months</strong>