update to frame and tarot times
This commit is contained in:
1
app.js
1
app.js
@@ -448,6 +448,7 @@ window.TarotSpreadUi?.init?.({
|
|||||||
window.TarotFrameUi?.init?.({
|
window.TarotFrameUi?.init?.({
|
||||||
ensureTarotSection,
|
ensureTarotSection,
|
||||||
getCards: () => window.TarotSectionUi?.getCards?.() || [],
|
getCards: () => window.TarotSectionUi?.getCards?.() || [],
|
||||||
|
openCardLightbox: (cardId, options = {}) => window.TarotSectionUi?.openCardLightboxById?.(cardId, options),
|
||||||
getHouseTopCardsVisible: () => window.TarotSectionUi?.getHouseTopCardsVisible?.() !== false,
|
getHouseTopCardsVisible: () => window.TarotSectionUi?.getHouseTopCardsVisible?.() !== false,
|
||||||
getHouseTopInfoModes: () => window.TarotSectionUi?.getHouseTopInfoModes?.() || {},
|
getHouseTopInfoModes: () => window.TarotSectionUi?.getHouseTopInfoModes?.() || {},
|
||||||
getHouseBottomCardsVisible: () => window.TarotSectionUi?.getHouseBottomCardsVisible?.() !== false,
|
getHouseBottomCardsVisible: () => window.TarotSectionUi?.getHouseBottomCardsVisible?.() !== false,
|
||||||
|
|||||||
@@ -1286,6 +1286,8 @@
|
|||||||
transition: transform 120ms ease, filter 120ms ease;
|
transition: transform 120ms ease, filter 120ms ease;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
touch-action: none;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tarot-frame-card.is-empty {
|
.tarot-frame-card.is-empty {
|
||||||
@@ -1306,6 +1308,17 @@
|
|||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (pointer: coarse) {
|
||||||
|
.tarot-frame-card {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tarot-frame-card:hover {
|
||||||
|
transform: none;
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tarot-frame-card-image,
|
.tarot-frame-card-image,
|
||||||
.tarot-frame-card-fallback {
|
.tarot-frame-card-fallback {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -212,6 +212,10 @@
|
|||||||
const windowDecans = windowDecanIds
|
const windowDecans = windowDecanIds
|
||||||
.map((decanId) => decanById.get(decanId) || null)
|
.map((decanId) => decanById.get(decanId) || null)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
const windowSignIds = tarotDb.courtSignWindows?.[cardName] || [];
|
||||||
|
const windowSigns = windowSignIds
|
||||||
|
.map((signId) => signById[signId] || null)
|
||||||
|
.filter(Boolean);
|
||||||
const explicitWindowRange = buildTokenDateRange(
|
const explicitWindowRange = buildTokenDateRange(
|
||||||
tarotDb.courtDateRanges?.[cardName]?.start,
|
tarotDb.courtDateRanges?.[cardName]?.start,
|
||||||
tarotDb.courtDateRanges?.[cardName]?.end
|
tarotDb.courtDateRanges?.[cardName]?.end
|
||||||
@@ -240,6 +244,24 @@
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (meta?.decan?.tarotMinorArcana) {
|
||||||
|
dynamicRelations.push(
|
||||||
|
createRelation(
|
||||||
|
"tarotCard",
|
||||||
|
`${meta.signId}-${meta.index}-${rankKey}-${suitKey}-decan-card`,
|
||||||
|
`Decan card: ${meta.decan.tarotMinorArcana} (${meta.signName} decan ${meta.index})`,
|
||||||
|
{
|
||||||
|
cardName: meta.decan.tarotMinorArcana,
|
||||||
|
decanId: meta.decan.id || `${meta.signId}-${meta.index}`,
|
||||||
|
signId: meta.signId,
|
||||||
|
signName: meta.signName,
|
||||||
|
decanIndex: meta.index,
|
||||||
|
dateRange: meta.dateRange?.label || null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const ruler = planets?.[meta?.decan?.rulerPlanetId] || null;
|
const ruler = planets?.[meta?.decan?.rulerPlanetId] || null;
|
||||||
if (ruler) {
|
if (ruler) {
|
||||||
dynamicRelations.push(
|
dynamicRelations.push(
|
||||||
@@ -290,9 +312,87 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (windowDecans.length) {
|
windowSigns.forEach((sign) => {
|
||||||
const firstRange = windowDecans[0].dateRange;
|
const signDateRange = buildTokenDateRange(sign?.start, sign?.end);
|
||||||
const lastRange = windowDecans[windowDecans.length - 1].dateRange;
|
const signName = sign?.name || sign?.id || "";
|
||||||
|
const signSymbol = sign?.symbol || "";
|
||||||
|
const relatedDecans = Array.isArray(decansBySign?.[sign.id]) ? decansBySign[sign.id] : [];
|
||||||
|
|
||||||
|
dynamicRelations.push(
|
||||||
|
createRelation(
|
||||||
|
"signWindow",
|
||||||
|
`${sign.id}-${rankKey}-${suitKey}`,
|
||||||
|
`Sign window: ${signSymbol} ${signName} (0°–30°)${signDateRange ? ` · ${signDateRange.label}` : ""}`.trim(),
|
||||||
|
{
|
||||||
|
signId: sign.id,
|
||||||
|
signName,
|
||||||
|
signSymbol,
|
||||||
|
startDegree: 0,
|
||||||
|
endDegree: 30,
|
||||||
|
dateStart: signDateRange?.startToken || null,
|
||||||
|
dateEnd: signDateRange?.endToken || null,
|
||||||
|
dateRange: signDateRange?.label || null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
relatedDecans.forEach((decan) => {
|
||||||
|
if (!decan?.tarotMinorArcana) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicRelations.push(
|
||||||
|
createRelation(
|
||||||
|
"tarotCard",
|
||||||
|
`${sign.id}-${decan.id || decan.tarotMinorArcana}-${rankKey}-${suitKey}-sign-card`,
|
||||||
|
`Sign card: ${decan.tarotMinorArcana} (${signName})`,
|
||||||
|
{
|
||||||
|
cardName: decan.tarotMinorArcana,
|
||||||
|
decanId: decan.id || null,
|
||||||
|
signId: sign.id,
|
||||||
|
signName,
|
||||||
|
signSymbol,
|
||||||
|
dateRange: signDateRange?.label || null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (signDateRange?.start && signDateRange?.end) {
|
||||||
|
const monthNumbers = listMonthNumbersBetween(signDateRange.start, signDateRange.end);
|
||||||
|
monthNumbers.forEach((monthNo) => {
|
||||||
|
const monthId = monthIdByNumber[monthNo];
|
||||||
|
const monthName = monthNameByNumber[monthNo] || `Month ${monthNo}`;
|
||||||
|
const monthKey = `${monthId}:${sign.id}:sign-window`;
|
||||||
|
if (!monthId || monthKeys.has(monthKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
monthKeys.add(monthKey);
|
||||||
|
|
||||||
|
dynamicRelations.push(
|
||||||
|
createRelation(
|
||||||
|
"calendarMonth",
|
||||||
|
`${monthId}-${sign.id}-${rankKey}-${suitKey}-sign-window`,
|
||||||
|
`Calendar month: ${monthName} (${signName} sign window)`,
|
||||||
|
{
|
||||||
|
monthId,
|
||||||
|
name: monthName,
|
||||||
|
monthOrder: monthNo,
|
||||||
|
signId: sign.id,
|
||||||
|
signName,
|
||||||
|
dateRange: signDateRange?.label || null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (windowDecans.length || windowSigns.length) {
|
||||||
|
const firstRange = windowDecans.length ? windowDecans[0].dateRange : null;
|
||||||
|
const lastRange = windowDecans.length ? windowDecans[windowDecans.length - 1].dateRange : null;
|
||||||
|
const firstSignRange = windowSigns.length ? buildTokenDateRange(windowSigns[0]?.start, windowSigns[0]?.end) : null;
|
||||||
|
const lastSignRange = windowSigns.length ? buildTokenDateRange(windowSigns[windowSigns.length - 1]?.start, windowSigns[windowSigns.length - 1]?.end) : null;
|
||||||
const fallbackWindowRange = firstRange && lastRange
|
const fallbackWindowRange = firstRange && lastRange
|
||||||
? {
|
? {
|
||||||
start: firstRange.start,
|
start: firstRange.start,
|
||||||
@@ -301,7 +401,15 @@
|
|||||||
endToken: lastRange.endToken,
|
endToken: lastRange.endToken,
|
||||||
label: `${formatMonthDayLabel(firstRange.start)}–${formatMonthDayLabel(lastRange.end)}`
|
label: `${formatMonthDayLabel(firstRange.start)}–${formatMonthDayLabel(lastRange.end)}`
|
||||||
}
|
}
|
||||||
: null;
|
: (firstSignRange && lastSignRange
|
||||||
|
? {
|
||||||
|
start: firstSignRange.start,
|
||||||
|
end: lastSignRange.end,
|
||||||
|
startToken: firstSignRange.startToken,
|
||||||
|
endToken: lastSignRange.endToken,
|
||||||
|
label: `${formatMonthDayLabel(firstSignRange.start)}–${formatMonthDayLabel(lastSignRange.end)}`
|
||||||
|
}
|
||||||
|
: null);
|
||||||
const windowRange = explicitWindowRange || fallbackWindowRange;
|
const windowRange = explicitWindowRange || fallbackWindowRange;
|
||||||
const windowLabel = windowRange?.label || "--";
|
const windowLabel = windowRange?.label || "--";
|
||||||
|
|
||||||
@@ -314,7 +422,8 @@
|
|||||||
dateStart: windowRange?.startToken || null,
|
dateStart: windowRange?.startToken || null,
|
||||||
dateEnd: windowRange?.endToken || null,
|
dateEnd: windowRange?.endToken || null,
|
||||||
dateRange: windowLabel,
|
dateRange: windowLabel,
|
||||||
decanIds: windowDecanIds
|
decanIds: windowDecanIds,
|
||||||
|
signIds: windowSignIds
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
const rankInfo = config?.rankInfo && typeof config.rankInfo === "object" ? config.rankInfo : {};
|
const rankInfo = config?.rankInfo && typeof config.rankInfo === "object" ? config.rankInfo : {};
|
||||||
const courtInfo = config?.courtInfo && typeof config.courtInfo === "object" ? config.courtInfo : {};
|
const courtInfo = config?.courtInfo && typeof config.courtInfo === "object" ? config.courtInfo : {};
|
||||||
const courtDecanWindows = config?.courtDecanWindows && typeof config.courtDecanWindows === "object" ? config.courtDecanWindows : {};
|
const courtDecanWindows = config?.courtDecanWindows && typeof config.courtDecanWindows === "object" ? config.courtDecanWindows : {};
|
||||||
|
const courtSignWindows = config?.courtSignWindows && typeof config.courtSignWindows === "object" ? config.courtSignWindows : {};
|
||||||
const majorAliases = config?.majorAliases && typeof config.majorAliases === "object" ? config.majorAliases : {};
|
const majorAliases = config?.majorAliases && typeof config.majorAliases === "object" ? config.majorAliases : {};
|
||||||
const minorNumeralAliases = config?.minorNumeralAliases && typeof config.minorNumeralAliases === "object" ? config.minorNumeralAliases : {};
|
const minorNumeralAliases = config?.minorNumeralAliases && typeof config.minorNumeralAliases === "object" ? config.minorNumeralAliases : {};
|
||||||
const monthNameByNumber = config?.monthNameByNumber && typeof config.monthNameByNumber === "object" ? config.monthNameByNumber : {};
|
const monthNameByNumber = config?.monthNameByNumber && typeof config.monthNameByNumber === "object" ? config.monthNameByNumber : {};
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
rankInfo: hasDb && db.rankInfo && typeof db.rankInfo === "object" ? db.rankInfo : rankInfo,
|
rankInfo: hasDb && db.rankInfo && typeof db.rankInfo === "object" ? db.rankInfo : rankInfo,
|
||||||
courtInfo: hasDb && db.courtInfo && typeof db.courtInfo === "object" ? db.courtInfo : courtInfo,
|
courtInfo: hasDb && db.courtInfo && typeof db.courtInfo === "object" ? db.courtInfo : courtInfo,
|
||||||
courtDecanWindows: hasDb && db.courtDecanWindows && typeof db.courtDecanWindows === "object" ? db.courtDecanWindows : courtDecanWindows,
|
courtDecanWindows: hasDb && db.courtDecanWindows && typeof db.courtDecanWindows === "object" ? db.courtDecanWindows : courtDecanWindows,
|
||||||
|
courtSignWindows: hasDb && db.courtSignWindows && typeof db.courtSignWindows === "object" ? db.courtSignWindows : courtSignWindows,
|
||||||
courtDateRanges: hasDb && db.courtDateRanges && typeof db.courtDateRanges === "object" ? db.courtDateRanges : {}
|
courtDateRanges: hasDb && db.courtDateRanges && typeof db.courtDateRanges === "object" ? db.courtDateRanges : {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -397,6 +397,13 @@
|
|||||||
"Prince of Cups": ["libra-3", "scorpio-1", "scorpio-2"]
|
"Prince of Cups": ["libra-3", "scorpio-1", "scorpio-2"]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const COURT_SIGN_WINDOWS = {
|
||||||
|
"Princess of Wands": ["cancer", "leo", "virgo"],
|
||||||
|
"Princess of Cups": ["libra", "scorpio", "sagittarius"],
|
||||||
|
"Princess of Swords": ["capricorn", "aquarius", "pisces"],
|
||||||
|
"Princess of Disks": ["aries", "taurus", "gemini"]
|
||||||
|
};
|
||||||
|
|
||||||
const MONTH_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
const MONTH_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||||
|
|
||||||
const MAJOR_HEBREW_LETTER_ID_BY_CARD = {
|
const MAJOR_HEBREW_LETTER_ID_BY_CARD = {
|
||||||
@@ -474,6 +481,7 @@
|
|||||||
rankInfo: RANK_INFO,
|
rankInfo: RANK_INFO,
|
||||||
courtInfo: COURT_INFO,
|
courtInfo: COURT_INFO,
|
||||||
courtDecanWindows: COURT_DECAN_WINDOWS,
|
courtDecanWindows: COURT_DECAN_WINDOWS,
|
||||||
|
courtSignWindows: COURT_SIGN_WINDOWS,
|
||||||
majorAliases: MAJOR_ALIASES,
|
majorAliases: MAJOR_ALIASES,
|
||||||
minorNumeralAliases: MINOR_NUMERAL_ALIASES,
|
minorNumeralAliases: MINOR_NUMERAL_ALIASES,
|
||||||
monthNameByNumber: MONTH_NAME_BY_NUMBER,
|
monthNameByNumber: MONTH_NAME_BY_NUMBER,
|
||||||
|
|||||||
@@ -20,6 +20,152 @@
|
|||||||
findSephirahForMinorCard
|
findSephirahForMinorCard
|
||||||
} = dependencies || {};
|
} = dependencies || {};
|
||||||
|
|
||||||
|
function buildDecanRelationKey(signId, decanIndex) {
|
||||||
|
const normalizedSignId = String(signId || "").trim().toLowerCase();
|
||||||
|
const normalizedIndex = Number(decanIndex);
|
||||||
|
if (!normalizedSignId || !Number.isFinite(normalizedIndex)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${normalizedSignId}-${normalizedIndex}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSignRelationKey(signId) {
|
||||||
|
return String(signId || "").trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDecanSummaryRelations(relations) {
|
||||||
|
const decanRelations = (relations || []).filter((relation) => relation?.type === "decan");
|
||||||
|
const signWindowRelations = (relations || []).filter((relation) => relation?.type === "signWindow");
|
||||||
|
if (!decanRelations.length && !signWindowRelations.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeInlineDateRange(value) {
|
||||||
|
return String(value || "")
|
||||||
|
.replace(/[–—]/g, " - ")
|
||||||
|
.replace(/\s*-\s*/g, " - ")
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
const rulerByDecanKey = new Map();
|
||||||
|
const cardByDecanKey = new Map();
|
||||||
|
const cardsBySignKey = new Map();
|
||||||
|
|
||||||
|
(relations || []).forEach((relation) => {
|
||||||
|
if (relation?.type === "decanRuler") {
|
||||||
|
const key = buildDecanRelationKey(relation?.data?.signId, relation?.data?.decanIndex);
|
||||||
|
if (key && !rulerByDecanKey.has(key)) {
|
||||||
|
rulerByDecanKey.set(key, relation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relation?.type === "tarotCard") {
|
||||||
|
const decanKey = buildDecanRelationKey(relation?.data?.signId, relation?.data?.decanIndex);
|
||||||
|
if (decanKey && !cardByDecanKey.has(decanKey)) {
|
||||||
|
cardByDecanKey.set(decanKey, relation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const signKey = buildSignRelationKey(relation?.data?.signId);
|
||||||
|
if (!signKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = cardsBySignKey.get(signKey) || [];
|
||||||
|
entries.push(relation);
|
||||||
|
cardsBySignKey.set(signKey, entries);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const decanSummaries = decanRelations.map((relation) => {
|
||||||
|
const signId = relation?.data?.signId;
|
||||||
|
const signName = String(relation?.data?.signName || "").trim();
|
||||||
|
const signSymbol = String(relation?.data?.signSymbol || relation?.data?.symbol || "").trim();
|
||||||
|
const decanIndex = Number(relation?.data?.index);
|
||||||
|
const startDegree = Number(relation?.data?.startDegree);
|
||||||
|
const endDegree = Number(relation?.data?.endDegree);
|
||||||
|
const dateRange = String(relation?.data?.dateRange || "").trim();
|
||||||
|
const decanKey = buildDecanRelationKey(signId, decanIndex);
|
||||||
|
const rulerRelation = rulerByDecanKey.get(decanKey) || null;
|
||||||
|
const cardRelation = cardByDecanKey.get(decanKey) || null;
|
||||||
|
const rulerSymbol = String(rulerRelation?.data?.symbol || "").trim();
|
||||||
|
const rulerName = String(rulerRelation?.data?.name || "").trim();
|
||||||
|
const rulerLabel = `${rulerSymbol} ${rulerName}`.replace(/\s+/g, " ").trim();
|
||||||
|
const decanCardName = String(cardRelation?.data?.cardName || "").trim();
|
||||||
|
const decanCardLabel = decanCardName
|
||||||
|
? String(getDisplayCardName?.(decanCardName) || decanCardName).trim()
|
||||||
|
: "";
|
||||||
|
const signLabel = `${signSymbol} ${signName}`.replace(/\s+/g, " ").trim();
|
||||||
|
const degreeLabel = Number.isFinite(startDegree) && Number.isFinite(endDegree)
|
||||||
|
? `(${startDegree}°-${endDegree}°)`
|
||||||
|
: "";
|
||||||
|
const dateLabel = normalizeInlineDateRange(dateRange);
|
||||||
|
const summaryParts = [
|
||||||
|
rulerLabel,
|
||||||
|
decanCardLabel,
|
||||||
|
[signLabel, degreeLabel].filter(Boolean).join(" ").trim(),
|
||||||
|
dateLabel
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: decanCardName ? "tarotCard" : "decan",
|
||||||
|
id: cardRelation?.id || `${decanKey}-summary`,
|
||||||
|
label: summaryParts.join(": "),
|
||||||
|
data: {
|
||||||
|
...(cardRelation?.data || relation?.data || {}),
|
||||||
|
signId,
|
||||||
|
signName,
|
||||||
|
signSymbol,
|
||||||
|
decanIndex,
|
||||||
|
dateRange,
|
||||||
|
cardName: decanCardName || cardRelation?.data?.cardName || ""
|
||||||
|
},
|
||||||
|
__key: `decanSummary|${decanKey}|${cardRelation?.data?.cardName || ""}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const signSummaries = signWindowRelations.map((relation) => {
|
||||||
|
const signId = relation?.data?.signId;
|
||||||
|
const signKey = buildSignRelationKey(signId);
|
||||||
|
const signName = String(relation?.data?.signName || "").trim();
|
||||||
|
const signSymbol = String(relation?.data?.signSymbol || relation?.data?.symbol || "").trim();
|
||||||
|
const startDegree = Number(relation?.data?.startDegree);
|
||||||
|
const endDegree = Number(relation?.data?.endDegree);
|
||||||
|
const dateRange = String(relation?.data?.dateRange || "").trim();
|
||||||
|
const cardLabels = [...new Set((cardsBySignKey.get(signKey) || [])
|
||||||
|
.map((entry) => String(getDisplayCardName?.(entry?.data?.cardName) || entry?.data?.cardName || "").trim())
|
||||||
|
.filter(Boolean))];
|
||||||
|
const signLabel = `${signSymbol} ${signName}`.replace(/\s+/g, " ").trim();
|
||||||
|
const degreeLabel = Number.isFinite(startDegree) && Number.isFinite(endDegree)
|
||||||
|
? `(${startDegree}°-${endDegree}°)`
|
||||||
|
: "";
|
||||||
|
const dateLabel = normalizeInlineDateRange(dateRange);
|
||||||
|
const summaryParts = [
|
||||||
|
cardLabels.join(", "),
|
||||||
|
[signLabel, degreeLabel].filter(Boolean).join(" ").trim(),
|
||||||
|
dateLabel
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "signWindow",
|
||||||
|
id: relation?.id || `${signKey}-summary`,
|
||||||
|
label: summaryParts.join(": "),
|
||||||
|
data: {
|
||||||
|
...(relation?.data || {}),
|
||||||
|
signId,
|
||||||
|
signName,
|
||||||
|
signSymbol,
|
||||||
|
cardNames: cardLabels
|
||||||
|
},
|
||||||
|
__key: `signSummary|${signKey}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...decanSummaries, ...signSummaries];
|
||||||
|
}
|
||||||
|
|
||||||
function renderStaticRelationGroup(targetEl, cardEl, relations) {
|
function renderStaticRelationGroup(targetEl, cardEl, relations) {
|
||||||
clearChildren(targetEl);
|
clearChildren(targetEl);
|
||||||
if (!targetEl || !cardEl) return;
|
if (!targetEl || !cardEl) return;
|
||||||
@@ -48,12 +194,20 @@
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const decanSummaryRelations = buildDecanSummaryRelations(dedupedRelations);
|
||||||
|
const hasDecanSummaryRelations = decanSummaryRelations.length > 0;
|
||||||
|
const hasSignWindowRelations = decanSummaryRelations.some((relation) => relation?.type === "signWindow");
|
||||||
|
|
||||||
const planetRelations = dedupedRelations.filter((relation) =>
|
const planetRelations = dedupedRelations.filter((relation) =>
|
||||||
relation.type === "planetCorrespondence" || relation.type === "decanRuler" || relation.type === "planet"
|
relation.type === "planetCorrespondence"
|
||||||
|
|| relation.type === "planet"
|
||||||
|
|| (!hasDecanSummaryRelations && relation.type === "decanRuler")
|
||||||
);
|
);
|
||||||
|
|
||||||
const zodiacRelations = dedupedRelations.filter((relation) =>
|
const zodiacRelations = dedupedRelations.filter((relation) =>
|
||||||
relation.type === "zodiacCorrespondence" || relation.type === "zodiac" || relation.type === "decan"
|
relation.type === "zodiacCorrespondence"
|
||||||
|
|| relation.type === "zodiac"
|
||||||
|
|| (!hasDecanSummaryRelations && relation.type === "decan")
|
||||||
);
|
);
|
||||||
|
|
||||||
const courtDateRelations = dedupedRelations.filter((relation) => relation.type === "courtDateWindow");
|
const courtDateRelations = dedupedRelations.filter((relation) => relation.type === "courtDateWindow");
|
||||||
@@ -63,9 +217,9 @@
|
|||||||
const elementRelations = buildElementRelationsForCard(card, baseElementRelations);
|
const elementRelations = buildElementRelationsForCard(card, baseElementRelations);
|
||||||
const tetragrammatonRelations = buildTetragrammatonRelationsForCard(card);
|
const tetragrammatonRelations = buildTetragrammatonRelationsForCard(card);
|
||||||
const smallCardRulershipRelation = buildSmallCardRulershipRelation(card);
|
const smallCardRulershipRelation = buildSmallCardRulershipRelation(card);
|
||||||
const zodiacRelationsWithRulership = smallCardRulershipRelation
|
const zodiacRelationsWithRulership = hasDecanSummaryRelations
|
||||||
? [...zodiacRelations, smallCardRulershipRelation]
|
? [...decanSummaryRelations, ...(smallCardRulershipRelation ? [smallCardRulershipRelation] : [])]
|
||||||
: zodiacRelations;
|
: [...zodiacRelations, ...(smallCardRulershipRelation ? [smallCardRulershipRelation] : [])];
|
||||||
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);
|
||||||
@@ -166,6 +320,8 @@
|
|||||||
elementRelations,
|
elementRelations,
|
||||||
tetragrammatonRelations,
|
tetragrammatonRelations,
|
||||||
zodiacRelationsWithRulership,
|
zodiacRelationsWithRulership,
|
||||||
|
hasDecanSummaryRelations,
|
||||||
|
hasSignWindowRelations,
|
||||||
mergedCourtDateRelations,
|
mergedCourtDateRelations,
|
||||||
hebrewRelations,
|
hebrewRelations,
|
||||||
cubeRelations,
|
cubeRelations,
|
||||||
@@ -183,7 +339,10 @@
|
|||||||
const groups = [
|
const groups = [
|
||||||
{ title: "Letter", relations: detailRelations.hebrewRelations },
|
{ title: "Letter", relations: detailRelations.hebrewRelations },
|
||||||
{ title: "Planet / Ruler", relations: detailRelations.planetRelations },
|
{ title: "Planet / Ruler", relations: detailRelations.planetRelations },
|
||||||
{ title: "Sign / Decan", relations: detailRelations.zodiacRelationsWithRulership },
|
{
|
||||||
|
title: detailRelations.hasSignWindowRelations ? "Signs" : (detailRelations.hasDecanSummaryRelations ? "Decan" : "Sign / Decan"),
|
||||||
|
relations: detailRelations.zodiacRelationsWithRulership
|
||||||
|
},
|
||||||
{ title: "Element", relations: detailRelations.elementRelations },
|
{ title: "Element", relations: detailRelations.elementRelations },
|
||||||
{ title: "Tetragrammaton", relations: detailRelations.tetragrammatonRelations },
|
{ title: "Tetragrammaton", relations: detailRelations.tetragrammatonRelations },
|
||||||
{ title: "Dates", relations: detailRelations.mergedCourtDateRelations },
|
{ title: "Dates", relations: detailRelations.mergedCourtDateRelations },
|
||||||
|
|||||||
@@ -192,6 +192,7 @@
|
|||||||
let config = {
|
let config = {
|
||||||
ensureTarotSection: null,
|
ensureTarotSection: null,
|
||||||
getCards: () => [],
|
getCards: () => [],
|
||||||
|
openCardLightbox: () => {},
|
||||||
getHouseTopCardsVisible: () => true,
|
getHouseTopCardsVisible: () => true,
|
||||||
getHouseTopInfoModes: () => ({}),
|
getHouseTopInfoModes: () => ({}),
|
||||||
getHouseBottomCardsVisible: () => true,
|
getHouseBottomCardsVisible: () => true,
|
||||||
@@ -1449,6 +1450,16 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.drag.sourceButton instanceof HTMLElement && typeof state.drag.sourceButton.releasePointerCapture === "function") {
|
||||||
|
try {
|
||||||
|
if (state.drag.sourceButton.hasPointerCapture?.(state.drag.pointerId)) {
|
||||||
|
state.drag.sourceButton.releasePointerCapture(state.drag.pointerId);
|
||||||
|
}
|
||||||
|
} catch (_error) {
|
||||||
|
// Ignore pointer-capture release failures during cleanup.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setHoverSlot("");
|
setHoverSlot("");
|
||||||
getSlotElement(state.drag.sourceSlotId)?.classList.remove("is-drag-source");
|
getSlotElement(state.drag.sourceSlotId)?.classList.remove("is-drag-source");
|
||||||
if (state.drag.ghostEl instanceof HTMLElement) {
|
if (state.drag.ghostEl instanceof HTMLElement) {
|
||||||
@@ -1482,6 +1493,13 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof config.openCardLightbox === "function") {
|
||||||
|
config.openCardLightbox(getCardId(card), {
|
||||||
|
onSelectCardId: () => {}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const deckOptions = resolveDeckOptions(card);
|
const deckOptions = resolveDeckOptions(card);
|
||||||
const src = String(
|
const src = String(
|
||||||
tarotCardImages.resolveTarotCardImage?.(card.name, deckOptions)
|
tarotCardImages.resolveTarotCardImage?.(card.name, deckOptions)
|
||||||
@@ -1522,9 +1540,22 @@
|
|||||||
startY: event.clientY,
|
startY: event.clientY,
|
||||||
started: false,
|
started: false,
|
||||||
hoverSlotId: "",
|
hoverSlotId: "",
|
||||||
ghostEl: null
|
ghostEl: null,
|
||||||
|
sourceButton: cardButton
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (typeof cardButton.setPointerCapture === "function") {
|
||||||
|
try {
|
||||||
|
cardButton.setPointerCapture(event.pointerId);
|
||||||
|
} catch (_error) {
|
||||||
|
// Ignore pointer-capture failures and continue with document listeners.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String(event.pointerType || "").toLowerCase() === "touch") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
detachPointerListeners();
|
detachPointerListeners();
|
||||||
document.addEventListener("pointermove", handlePointerMove);
|
document.addEventListener("pointermove", handlePointerMove);
|
||||||
document.addEventListener("pointerup", handlePointerUp);
|
document.addEventListener("pointerup", handlePointerUp);
|
||||||
|
|||||||
@@ -795,6 +795,48 @@
|
|||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openCardLightboxById(cardIdToOpen, options = {}) {
|
||||||
|
const normalizedCardId = String(cardIdToOpen || "").trim();
|
||||||
|
if (!normalizedCardId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const primaryCardRequest = buildLightboxCardRequestById(normalizedCardId);
|
||||||
|
if (!primaryCardRequest?.src) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeDeckId = String(getActiveDeck?.() || primaryCardRequest.deckId || "").trim();
|
||||||
|
const availableCompareDecks = getRegisteredDeckList().filter((deck) => deck.id && deck.id !== activeDeckId);
|
||||||
|
const onSelectCardId = typeof options?.onSelectCardId === "function"
|
||||||
|
? options.onSelectCardId
|
||||||
|
: (nextCardId) => {
|
||||||
|
const latestElements = getElements();
|
||||||
|
selectCardById(nextCardId, latestElements);
|
||||||
|
scrollCardIntoView(nextCardId, latestElements);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.TarotUiLightbox?.open?.({
|
||||||
|
src: primaryCardRequest.src,
|
||||||
|
altText: primaryCardRequest.altText,
|
||||||
|
label: primaryCardRequest.label,
|
||||||
|
cardId: primaryCardRequest.cardId,
|
||||||
|
deckId: primaryCardRequest.deckId || activeDeckId,
|
||||||
|
deckLabel: primaryCardRequest.deckLabel || "",
|
||||||
|
compareDetails: primaryCardRequest.compareDetails || [],
|
||||||
|
allowOverlayCompare: true,
|
||||||
|
allowDeckCompare: true,
|
||||||
|
activeDeckId,
|
||||||
|
activeDeckLabel: primaryCardRequest.deckLabel || "",
|
||||||
|
availableCompareDecks,
|
||||||
|
maxCompareDecks: 2,
|
||||||
|
sequenceIds: state.cards.map((card) => card.id),
|
||||||
|
resolveCardById: buildLightboxCardRequestById,
|
||||||
|
resolveDeckCardById: buildDeckLightboxCardRequest,
|
||||||
|
onSelectCardId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function renderList(elements) {
|
function renderList(elements) {
|
||||||
if (!elements?.tarotCardListEl) {
|
if (!elements?.tarotCardListEl) {
|
||||||
return;
|
return;
|
||||||
@@ -858,31 +900,15 @@
|
|||||||
selectCardById,
|
selectCardById,
|
||||||
openCardLightbox: (src, altText, options = {}) => {
|
openCardLightbox: (src, altText, options = {}) => {
|
||||||
const cardId = String(options?.cardId || "").trim();
|
const cardId = String(options?.cardId || "").trim();
|
||||||
const primaryCardRequest = cardId ? buildLightboxCardRequestById(cardId) : null;
|
if (cardId) {
|
||||||
const activeDeckId = String(getActiveDeck?.() || primaryCardRequest?.deckId || "").trim();
|
openCardLightboxById(cardId);
|
||||||
const availableCompareDecks = getRegisteredDeckList().filter((deck) => deck.id && deck.id !== activeDeckId);
|
return;
|
||||||
window.TarotUiLightbox?.open?.({
|
|
||||||
src: primaryCardRequest?.src || src,
|
|
||||||
altText: primaryCardRequest?.altText || altText || "Tarot card enlarged image",
|
|
||||||
label: primaryCardRequest?.label || altText || "Tarot card enlarged image",
|
|
||||||
cardId: primaryCardRequest?.cardId || cardId,
|
|
||||||
deckId: primaryCardRequest?.deckId || activeDeckId,
|
|
||||||
deckLabel: primaryCardRequest?.deckLabel || "",
|
|
||||||
compareDetails: primaryCardRequest?.compareDetails || [],
|
|
||||||
allowOverlayCompare: true,
|
|
||||||
allowDeckCompare: Boolean(cardId),
|
|
||||||
activeDeckId,
|
|
||||||
activeDeckLabel: primaryCardRequest?.deckLabel || "",
|
|
||||||
availableCompareDecks,
|
|
||||||
maxCompareDecks: 2,
|
|
||||||
sequenceIds: state.cards.map((card) => card.id),
|
|
||||||
resolveCardById: buildLightboxCardRequestById,
|
|
||||||
resolveDeckCardById: buildDeckLightboxCardRequest,
|
|
||||||
onSelectCardId: (nextCardId) => {
|
|
||||||
const latestElements = getElements();
|
|
||||||
selectCardById(nextCardId, latestElements);
|
|
||||||
scrollCardIntoView(nextCardId, latestElements);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.TarotUiLightbox?.open?.({
|
||||||
|
src,
|
||||||
|
altText: altText || "Tarot card enlarged image",
|
||||||
|
label: altText || "Tarot card enlarged image"
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
shouldOpenCardLightboxOnSelect: (latestElements) => Boolean(
|
shouldOpenCardLightboxOnSelect: (latestElements) => Boolean(
|
||||||
@@ -1116,6 +1142,7 @@
|
|||||||
ensureTarotSection,
|
ensureTarotSection,
|
||||||
selectCardByTrump,
|
selectCardByTrump,
|
||||||
selectCardByName,
|
selectCardByName,
|
||||||
|
openCardLightboxById,
|
||||||
getCards: () => state.cards,
|
getCards: () => state.cards,
|
||||||
getHouseTopCardsVisible: () => state.houseTopCardsVisible,
|
getHouseTopCardsVisible: () => state.houseTopCardsVisible,
|
||||||
getHouseTopInfoModes: () => ({ ...state.houseTopInfoModes }),
|
getHouseTopInfoModes: () => ({ ...state.houseTopInfoModes }),
|
||||||
|
|||||||
16
index.html
16
index.html
@@ -16,7 +16,7 @@
|
|||||||
<link rel="stylesheet" href="node_modules/@fontsource/amiri/arabic-400.css">
|
<link rel="stylesheet" href="node_modules/@fontsource/amiri/arabic-400.css">
|
||||||
<link rel="stylesheet" href="node_modules/@fontsource/amiri/arabic-700.css">
|
<link rel="stylesheet" href="node_modules/@fontsource/amiri/arabic-700.css">
|
||||||
<link rel="stylesheet" href="node_modules/@fontsource/noto-naskh-arabic/arabic-400.css">
|
<link rel="stylesheet" href="node_modules/@fontsource/noto-naskh-arabic/arabic-400.css">
|
||||||
<link rel="stylesheet" href="app/styles.css?v=20260402-frame-center-07">
|
<link rel="stylesheet" href="app/styles.css?v=20260402-frame-center-09">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
@@ -1124,9 +1124,9 @@
|
|||||||
<script src="app/ui-now-helpers.js?v=20260314-now-planets-grid-01"></script>
|
<script src="app/ui-now-helpers.js?v=20260314-now-planets-grid-01"></script>
|
||||||
<script src="app/ui-now.js?v=20260314-now-planets-grid-01"></script>
|
<script src="app/ui-now.js?v=20260314-now-planets-grid-01"></script>
|
||||||
<script src="app/ui-natal.js"></script>
|
<script src="app/ui-natal.js"></script>
|
||||||
<script src="app/tarot-database-builders.js?v=20260401-tarot-dates-01"></script>
|
<script src="app/tarot-database-builders.js?v=20260402-princess-links-01"></script>
|
||||||
<script src="app/tarot-database-assembly.js?v=20260401-tarot-dates-01"></script>
|
<script src="app/tarot-database-assembly.js?v=20260402-princess-links-01"></script>
|
||||||
<script src="app/tarot-database.js?v=20260401-tarot-dates-01"></script>
|
<script src="app/tarot-database.js?v=20260402-princess-links-01"></script>
|
||||||
<script src="app/ui-calendar-dates.js"></script>
|
<script src="app/ui-calendar-dates.js"></script>
|
||||||
<script src="app/ui-calendar-detail-panels.js"></script>
|
<script src="app/ui-calendar-detail-panels.js"></script>
|
||||||
<script src="app/ui-calendar-detail.js"></script>
|
<script src="app/ui-calendar-detail.js"></script>
|
||||||
@@ -1136,9 +1136,9 @@
|
|||||||
<script src="app/ui-holidays-render.js"></script>
|
<script src="app/ui-holidays-render.js"></script>
|
||||||
<script src="app/ui-holidays.js"></script>
|
<script src="app/ui-holidays.js"></script>
|
||||||
<script src="app/ui-tarot-card-derivations.js?v=20260307b"></script>
|
<script src="app/ui-tarot-card-derivations.js?v=20260307b"></script>
|
||||||
<script src="app/ui-tarot-detail.js?v=20260307b"></script>
|
<script src="app/ui-tarot-detail.js?v=20260402-princess-links-01"></script>
|
||||||
<script src="app/ui-tarot-relation-display.js?v=20260307b"></script>
|
<script src="app/ui-tarot-relation-display.js?v=20260307b"></script>
|
||||||
<script src="app/ui-tarot.js?v=20260401-house-top-date-02"></script>
|
<script src="app/ui-tarot.js?v=20260402-frame-lightbox-01"></script>
|
||||||
<script src="app/ui-planets-references.js"></script>
|
<script src="app/ui-planets-references.js"></script>
|
||||||
<script src="app/ui-planets.js"></script>
|
<script src="app/ui-planets.js"></script>
|
||||||
<script src="app/ui-cycles.js"></script>
|
<script src="app/ui-cycles.js"></script>
|
||||||
@@ -1178,7 +1178,7 @@
|
|||||||
<script src="app/ui-numbers-detail.js"></script>
|
<script src="app/ui-numbers-detail.js"></script>
|
||||||
<script src="app/ui-numbers.js"></script>
|
<script src="app/ui-numbers.js"></script>
|
||||||
<script src="app/ui-tarot-spread.js"></script>
|
<script src="app/ui-tarot-spread.js"></script>
|
||||||
<script src="app/ui-tarot-frame.js?v=20260402-frame-mobile-center-06"></script>
|
<script src="app/ui-tarot-frame.js?v=20260402-frame-lightbox-01"></script>
|
||||||
<script src="app/ui-settings.js?v=20260309-gate"></script>
|
<script src="app/ui-settings.js?v=20260309-gate"></script>
|
||||||
<script src="app/ui-chrome.js?v=20260328-topbar-settings-01"></script>
|
<script src="app/ui-chrome.js?v=20260328-topbar-settings-01"></script>
|
||||||
<script src="app/ui-navigation.js?v=20260401-tarot-frame-01"></script>
|
<script src="app/ui-navigation.js?v=20260401-tarot-frame-01"></script>
|
||||||
@@ -1187,7 +1187,7 @@
|
|||||||
<script src="app/ui-home-calendar.js"></script>
|
<script src="app/ui-home-calendar.js"></script>
|
||||||
<script src="app/ui-section-state.js?v=20260401-tarot-frame-01"></script>
|
<script src="app/ui-section-state.js?v=20260401-tarot-frame-01"></script>
|
||||||
<script src="app/app-runtime.js?v=20260309-gate"></script>
|
<script src="app/app-runtime.js?v=20260309-gate"></script>
|
||||||
<script src="app.js?v=20260402-global-zoom-01"></script>
|
<script src="app.js?v=20260402-frame-lightbox-01"></script>
|
||||||
<script src="app/navigation-detail-test-harness.js?v=20260401-universal-detail-02"></script>
|
<script src="app/navigation-detail-test-harness.js?v=20260401-universal-detail-02"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user