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?.({
|
||||
ensureTarotSection,
|
||||
getCards: () => window.TarotSectionUi?.getCards?.() || [],
|
||||
openCardLightbox: (cardId, options = {}) => window.TarotSectionUi?.openCardLightboxById?.(cardId, options),
|
||||
getHouseTopCardsVisible: () => window.TarotSectionUi?.getHouseTopCardsVisible?.() !== false,
|
||||
getHouseTopInfoModes: () => window.TarotSectionUi?.getHouseTopInfoModes?.() || {},
|
||||
getHouseBottomCardsVisible: () => window.TarotSectionUi?.getHouseBottomCardsVisible?.() !== false,
|
||||
|
||||
@@ -1286,6 +1286,8 @@
|
||||
transition: transform 120ms ease, filter 120ms ease;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
touch-action: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
.tarot-frame-card.is-empty {
|
||||
@@ -1306,6 +1308,17 @@
|
||||
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-fallback {
|
||||
display: block;
|
||||
|
||||
@@ -212,6 +212,10 @@
|
||||
const windowDecans = windowDecanIds
|
||||
.map((decanId) => decanById.get(decanId) || null)
|
||||
.filter(Boolean);
|
||||
const windowSignIds = tarotDb.courtSignWindows?.[cardName] || [];
|
||||
const windowSigns = windowSignIds
|
||||
.map((signId) => signById[signId] || null)
|
||||
.filter(Boolean);
|
||||
const explicitWindowRange = buildTokenDateRange(
|
||||
tarotDb.courtDateRanges?.[cardName]?.start,
|
||||
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;
|
||||
if (ruler) {
|
||||
dynamicRelations.push(
|
||||
@@ -290,9 +312,87 @@
|
||||
}
|
||||
});
|
||||
|
||||
if (windowDecans.length) {
|
||||
const firstRange = windowDecans[0].dateRange;
|
||||
const lastRange = windowDecans[windowDecans.length - 1].dateRange;
|
||||
windowSigns.forEach((sign) => {
|
||||
const signDateRange = buildTokenDateRange(sign?.start, sign?.end);
|
||||
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
|
||||
? {
|
||||
start: firstRange.start,
|
||||
@@ -301,7 +401,15 @@
|
||||
endToken: lastRange.endToken,
|
||||
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 windowLabel = windowRange?.label || "--";
|
||||
|
||||
@@ -314,7 +422,8 @@
|
||||
dateStart: windowRange?.startToken || null,
|
||||
dateEnd: windowRange?.endToken || null,
|
||||
dateRange: windowLabel,
|
||||
decanIds: windowDecanIds
|
||||
decanIds: windowDecanIds,
|
||||
signIds: windowSignIds
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
const rankInfo = config?.rankInfo && typeof config.rankInfo === "object" ? config.rankInfo : {};
|
||||
const courtInfo = config?.courtInfo && typeof config.courtInfo === "object" ? config.courtInfo : {};
|
||||
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 minorNumeralAliases = config?.minorNumeralAliases && typeof config.minorNumeralAliases === "object" ? config.minorNumeralAliases : {};
|
||||
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,
|
||||
courtInfo: hasDb && db.courtInfo && typeof db.courtInfo === "object" ? db.courtInfo : courtInfo,
|
||||
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 : {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -397,6 +397,13 @@
|
||||
"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 MAJOR_HEBREW_LETTER_ID_BY_CARD = {
|
||||
@@ -474,6 +481,7 @@
|
||||
rankInfo: RANK_INFO,
|
||||
courtInfo: COURT_INFO,
|
||||
courtDecanWindows: COURT_DECAN_WINDOWS,
|
||||
courtSignWindows: COURT_SIGN_WINDOWS,
|
||||
majorAliases: MAJOR_ALIASES,
|
||||
minorNumeralAliases: MINOR_NUMERAL_ALIASES,
|
||||
monthNameByNumber: MONTH_NAME_BY_NUMBER,
|
||||
|
||||
@@ -20,6 +20,152 @@
|
||||
findSephirahForMinorCard
|
||||
} = 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) {
|
||||
clearChildren(targetEl);
|
||||
if (!targetEl || !cardEl) return;
|
||||
@@ -48,12 +194,20 @@
|
||||
return true;
|
||||
});
|
||||
|
||||
const decanSummaryRelations = buildDecanSummaryRelations(dedupedRelations);
|
||||
const hasDecanSummaryRelations = decanSummaryRelations.length > 0;
|
||||
const hasSignWindowRelations = decanSummaryRelations.some((relation) => relation?.type === "signWindow");
|
||||
|
||||
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) =>
|
||||
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");
|
||||
@@ -63,9 +217,9 @@
|
||||
const elementRelations = buildElementRelationsForCard(card, baseElementRelations);
|
||||
const tetragrammatonRelations = buildTetragrammatonRelationsForCard(card);
|
||||
const smallCardRulershipRelation = buildSmallCardRulershipRelation(card);
|
||||
const zodiacRelationsWithRulership = smallCardRulershipRelation
|
||||
? [...zodiacRelations, smallCardRulershipRelation]
|
||||
: zodiacRelations;
|
||||
const zodiacRelationsWithRulership = hasDecanSummaryRelations
|
||||
? [...decanSummaryRelations, ...(smallCardRulershipRelation ? [smallCardRulershipRelation] : [])]
|
||||
: [...zodiacRelations, ...(smallCardRulershipRelation ? [smallCardRulershipRelation] : [])];
|
||||
const smallCardCourtLinkRelations = buildSmallCardCourtLinkRelations(card, dedupedRelations);
|
||||
const mergedCourtDateRelations = [...courtDateRelations, ...smallCardCourtLinkRelations];
|
||||
const cubeRelations = buildCubeRelationsForCard(card);
|
||||
@@ -166,6 +320,8 @@
|
||||
elementRelations,
|
||||
tetragrammatonRelations,
|
||||
zodiacRelationsWithRulership,
|
||||
hasDecanSummaryRelations,
|
||||
hasSignWindowRelations,
|
||||
mergedCourtDateRelations,
|
||||
hebrewRelations,
|
||||
cubeRelations,
|
||||
@@ -183,7 +339,10 @@
|
||||
const groups = [
|
||||
{ title: "Letter", relations: detailRelations.hebrewRelations },
|
||||
{ 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: "Tetragrammaton", relations: detailRelations.tetragrammatonRelations },
|
||||
{ title: "Dates", relations: detailRelations.mergedCourtDateRelations },
|
||||
|
||||
@@ -192,6 +192,7 @@
|
||||
let config = {
|
||||
ensureTarotSection: null,
|
||||
getCards: () => [],
|
||||
openCardLightbox: () => {},
|
||||
getHouseTopCardsVisible: () => true,
|
||||
getHouseTopInfoModes: () => ({}),
|
||||
getHouseBottomCardsVisible: () => true,
|
||||
@@ -1449,6 +1450,16 @@
|
||||
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("");
|
||||
getSlotElement(state.drag.sourceSlotId)?.classList.remove("is-drag-source");
|
||||
if (state.drag.ghostEl instanceof HTMLElement) {
|
||||
@@ -1482,6 +1493,13 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof config.openCardLightbox === "function") {
|
||||
config.openCardLightbox(getCardId(card), {
|
||||
onSelectCardId: () => {}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const deckOptions = resolveDeckOptions(card);
|
||||
const src = String(
|
||||
tarotCardImages.resolveTarotCardImage?.(card.name, deckOptions)
|
||||
@@ -1522,9 +1540,22 @@
|
||||
startY: event.clientY,
|
||||
started: false,
|
||||
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();
|
||||
document.addEventListener("pointermove", handlePointerMove);
|
||||
document.addEventListener("pointerup", handlePointerUp);
|
||||
|
||||
@@ -795,6 +795,48 @@
|
||||
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) {
|
||||
if (!elements?.tarotCardListEl) {
|
||||
return;
|
||||
@@ -858,31 +900,15 @@
|
||||
selectCardById,
|
||||
openCardLightbox: (src, altText, options = {}) => {
|
||||
const cardId = String(options?.cardId || "").trim();
|
||||
const primaryCardRequest = cardId ? buildLightboxCardRequestById(cardId) : null;
|
||||
const activeDeckId = String(getActiveDeck?.() || primaryCardRequest?.deckId || "").trim();
|
||||
const availableCompareDecks = getRegisteredDeckList().filter((deck) => deck.id && deck.id !== activeDeckId);
|
||||
if (cardId) {
|
||||
openCardLightboxById(cardId);
|
||||
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);
|
||||
}
|
||||
src,
|
||||
altText: altText || "Tarot card enlarged image",
|
||||
label: altText || "Tarot card enlarged image"
|
||||
});
|
||||
},
|
||||
shouldOpenCardLightboxOnSelect: (latestElements) => Boolean(
|
||||
@@ -1116,6 +1142,7 @@
|
||||
ensureTarotSection,
|
||||
selectCardByTrump,
|
||||
selectCardByName,
|
||||
openCardLightboxById,
|
||||
getCards: () => state.cards,
|
||||
getHouseTopCardsVisible: () => state.houseTopCardsVisible,
|
||||
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-700.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>
|
||||
<body>
|
||||
<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.js?v=20260314-now-planets-grid-01"></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-assembly.js?v=20260401-tarot-dates-01"></script>
|
||||
<script src="app/tarot-database.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=20260402-princess-links-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-detail-panels.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.js"></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.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.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.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-chrome.js?v=20260328-topbar-settings-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-section-state.js?v=20260401-tarot-frame-01"></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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user