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;
}
.iching-tarot-text {
white-space: pre-line;
white-space: normal;
line-height: 1.4;
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 ──────────────────────────────────────────────── */

View File

@@ -315,6 +315,8 @@
return;
}
clearChildren(elements.ichingDetailTarotEl);
const upperKey = normalizeSearchValue(entry?.upperTrigram);
const lowerKey = normalizeSearchValue(entry?.lowerTrigram);
const upperCards = upperKey ? state.tarotByTrigramName[upperKey] || [] : [];
@@ -325,15 +327,79 @@
const upperLabel = upperTrigram?.element || entry?.upperTrigram || "--";
const lowerLabel = lowerTrigram?.element || entry?.lowerTrigram || "--";
const lines = [];
if (upperKey) {
lines.push(`Upper (${upperLabel}): ${upperCards.length ? upperCards.join(", ") : "--"}`);
}
if (lowerKey) {
lines.push(`Lower (${lowerLabel}): ${lowerCards.length ? lowerCards.join(", ") : "--"}`);
function buildTarotTarget(cardName) {
const label = String(cardName || "").trim();
if (!label) {
return null;
}
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 }
};
}
elements.ichingDetailTarotEl.textContent = lines.length ? lines.join("\n\n") : "--";
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) {

View File

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

View File

@@ -190,6 +190,17 @@
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") {
const wallId = d.wallId || relation?.id;
if (!wallId) {

View File

@@ -102,6 +102,8 @@
return String(value || "")
.trim()
.toLowerCase()
.replace(/^key\s+\d+\s*:\s*/g, "")
.replace(/\b(pentacles?|coins?)\b/g, "disks")
.replace(/\s+/g, " ");
}
@@ -114,6 +116,14 @@
}
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);
if (!key) {
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 = {
buildCourtCardByDecanId,
buildSmallCardCourtLinkRelations,
buildMonthReferencesByCard,
buildCubeRelationsForCard,
buildIChingRelationsForCard,
parseMonthDayToken
};
})();

View File

@@ -236,6 +236,7 @@
tarotMetaCourtDateCardEl: document.getElementById("tarot-meta-courtdate-card"),
tarotMetaHebrewCardEl: document.getElementById("tarot-meta-hebrew-card"),
tarotMetaCubeCardEl: document.getElementById("tarot-meta-cube-card"),
tarotMetaIChingCardEl: document.getElementById("tarot-meta-iching-card"),
tarotMetaCalendarCardEl: document.getElementById("tarot-meta-calendar-card"),
tarotDetailPlanetEl: document.getElementById("tarot-detail-planet"),
tarotDetailElementEl: document.getElementById("tarot-detail-element"),
@@ -244,6 +245,7 @@
tarotDetailCourtDateEl: document.getElementById("tarot-detail-courtdate"),
tarotDetailHebrewEl: document.getElementById("tarot-detail-hebrew"),
tarotDetailCubeEl: document.getElementById("tarot-detail-cube"),
tarotDetailIChingEl: document.getElementById("tarot-detail-iching"),
tarotDetailCalendarEl: document.getElementById("tarot-detail-calendar"),
tarotKabPathEl: document.getElementById("tarot-kab-path"),
tarotHouseOfCardsEl: document.getElementById("tarot-house-of-cards")
@@ -303,6 +305,7 @@
buildSmallCardRulershipRelation,
buildSmallCardCourtLinkRelations,
buildCubeRelationsForCard,
buildIChingRelationsForCard,
parseMonthDayToken,
createRelationListItem,
findSephirahForMinorCard
@@ -513,6 +516,13 @@
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,
// null for informational-only relations.
function getRelationNavTarget(relation) {