update commit message

This commit is contained in:
2026-05-29 00:27:03 -07:00
parent ed1107a0c0
commit 254f488eca
21 changed files with 565 additions and 210 deletions
+46
View File
@@ -75,6 +75,7 @@ const openAudioNotesEl = document.getElementById("open-audio-notes");
const openTarotEl = document.getElementById("open-tarot"); const openTarotEl = document.getElementById("open-tarot");
const openTarotFrameEl = document.getElementById("open-tarot-frame"); const openTarotFrameEl = document.getElementById("open-tarot-frame");
const openTarotHouseEl = document.getElementById("open-tarot-house"); const openTarotHouseEl = document.getElementById("open-tarot-house");
const settingsTarotPanelEl = document.getElementById("settings-tarot-panel");
const openAstronomyEl = document.getElementById("open-astronomy"); const openAstronomyEl = document.getElementById("open-astronomy");
const openPlanetsEl = document.getElementById("open-planets"); const openPlanetsEl = document.getElementById("open-planets");
const openCyclesEl = document.getElementById("open-cycles"); const openCyclesEl = document.getElementById("open-cycles");
@@ -107,6 +108,9 @@ const connectionGateBaseUrlEl = document.getElementById("connection-gate-base-ur
const connectionGateApiKeyEl = document.getElementById("connection-gate-api-key"); const connectionGateApiKeyEl = document.getElementById("connection-gate-api-key");
const connectionGateStatusEl = document.getElementById("connection-gate-status"); const connectionGateStatusEl = document.getElementById("connection-gate-status");
const connectionGateConnectEl = document.getElementById("connection-gate-connect"); const connectionGateConnectEl = document.getElementById("connection-gate-connect");
const tarotDropdownEl = openTarotEl?.closest(".topbar-dropdown") || null;
const TAROT_RESTRICTED_SECTIONS = new Set(["tarot", "tarot-frame", "tarot-house"]);
const nowElements = { const nowElements = {
nowHourEl: document.getElementById("now-hour"), nowHourEl: document.getElementById("now-hour"),
@@ -128,6 +132,34 @@ const nowElements = {
nowStatsPlanetsEl: document.getElementById("now-stats-planets") nowStatsPlanetsEl: document.getElementById("now-stats-planets")
}; };
function hasTarotFeatureAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function isSectionAccessible(section) {
if (TAROT_RESTRICTED_SECTIONS.has(String(section || "").trim())) {
return hasTarotFeatureAccess();
}
return true;
}
function syncTarotFeatureVisibility() {
const tarotVisible = hasTarotFeatureAccess();
if (tarotDropdownEl) {
tarotDropdownEl.hidden = !tarotVisible;
}
if (settingsTarotPanelEl) {
settingsTarotPanelEl.hidden = !tarotVisible;
}
if (!tarotVisible && TAROT_RESTRICTED_SECTIONS.has(sectionStateUi.getActiveSection?.())) {
sectionStateUi.setActiveSection?.("home");
}
}
const baseWeekOptions = { const baseWeekOptions = {
hourStart: 0, hourStart: 0,
hourEnd: 24, hourEnd: 24,
@@ -245,6 +277,7 @@ appRuntime.init?.({
nowElements, nowElements,
calendarVisualsUi, calendarVisualsUi,
homeUi, homeUi,
hasTarotAccess: () => hasTarotFeatureAccess(),
onStatus: (text) => setStatus(text), onStatus: (text) => setStatus(text),
services: { services: {
getCenteredWeekStartDay, getCenteredWeekStartDay,
@@ -390,6 +423,10 @@ function getConnectionSettingsFromGate() {
} }
function warmAllDeckImagesInBackground() { function warmAllDeckImagesInBackground() {
if (!hasTarotFeatureAccess()) {
return;
}
const activeDeckId = String(window.TarotCardImages?.getActiveDeck?.() || "").trim(); const activeDeckId = String(window.TarotCardImages?.getActiveDeck?.() || "").trim();
window.TarotCardImages?.scheduleAllDeckImagePreload?.({ window.TarotCardImages?.scheduleAllDeckImagePreload?.({
@@ -426,6 +463,9 @@ async function ensureConnectedApp(nextConnectionSettings = null) {
syncConnectionGateInputs(configuredConnection); syncConnectionGateInputs(configuredConnection);
} }
window.TarotAppConfig?.updateConnectionAccess?.(probeResult);
syncTarotFeatureVisibility();
hideConnectionGate(); hideConnectionGate();
if (!hasRenderedConnectedShell) { if (!hasRenderedConnectedShell) {
sectionStateUi.setActiveSection?.("home"); sectionStateUi.setActiveSection?.("home");
@@ -500,6 +540,7 @@ sectionStateUi.init?.({
settingsUi, settingsUi,
calendarVisualsUi, calendarVisualsUi,
homeUi, homeUi,
isSectionAccessible: (section) => isSectionAccessible(section),
getReferenceData: () => appRuntime.getReferenceData?.() || null, getReferenceData: () => appRuntime.getReferenceData?.() || null,
getMagickDataset: () => appRuntime.getMagickDataset?.() || null, getMagickDataset: () => appRuntime.getMagickDataset?.() || null,
elements: { elements: {
@@ -637,6 +678,10 @@ if (nowOverlayToggleEl && nowPanelEl) {
syncNowOverlayVisibility(); syncNowOverlayVisibility();
} }
document.addEventListener("connection:access-updated", () => {
syncTarotFeatureVisibility();
});
navigationUi.init?.({ navigationUi.init?.({
tarotSpreadUi, tarotSpreadUi,
getActiveSection: () => sectionStateUi.getActiveSection?.() || "home", getActiveSection: () => sectionStateUi.getActiveSection?.() || "home",
@@ -714,5 +759,6 @@ window.TarotNatal = {
const initialSettings = settingsUi.loadInitialSettingsAndApply?.() || { ...DEFAULT_SETTINGS }; const initialSettings = settingsUi.loadInitialSettingsAndApply?.() || { ...DEFAULT_SETTINGS };
homeUi.syncNowSkyBackground?.({ latitude: initialSettings.latitude, longitude: initialSettings.longitude }, true); homeUi.syncNowSkyBackground?.({ latitude: initialSettings.latitude, longitude: initialSettings.longitude }, true);
syncTarotFeatureVisibility();
bindConnectionGate(); bindConnectionGate();
void ensureConnectedApp(); void ensureConnectedApp();
+90
View File
@@ -1,6 +1,20 @@
(function () { (function () {
const apiBaseUrlStorageKey = "tarot-time-api-base-url"; const apiBaseUrlStorageKey = "tarot-time-api-base-url";
const apiKeyStorageKey = "tarot-time-api-key"; const apiKeyStorageKey = "tarot-time-api-key";
const defaultConnectionAccess = Object.freeze({
connected: false,
apiKeyRequired: false,
authenticated: false,
clientId: "",
accountId: "",
accessLevel: "",
roles: Object.freeze([]),
scopes: Object.freeze([]),
capabilities: Object.freeze({
tarot: false,
adminApiManagement: false
})
});
function normalizeBaseUrl(value) { function normalizeBaseUrl(value) {
return String(value || "") return String(value || "")
@@ -12,6 +26,55 @@
return String(value || "").trim(); return String(value || "").trim();
} }
function normalizeStringList(values) {
return Array.isArray(values)
? values.map((value) => String(value || "").trim()).filter(Boolean)
: [];
}
function normalizeAccessLevel(value) {
return String(value || "").trim().toLowerCase();
}
function normalizeConnectionAccess(source = null) {
const health = source?.health || {};
const auth = health?.auth || source?.auth || {};
const roles = normalizeStringList(auth?.roles);
const scopes = normalizeStringList(auth?.scopes);
const apiKeyRequired = health?.apiKeyRequired === true || source?.apiKeyRequired === true;
const connected = source?.ok === true || source?.connected === true;
const accessLevel = normalizeAccessLevel(
auth?.accessLevel
|| source?.accessLevel
|| (connected && !apiKeyRequired ? "premium" : "")
);
const tarotCapability = source?.capabilities?.tarot === true
|| (connected && !apiKeyRequired)
|| accessLevel === "premium";
const adminApiManagementCapability = source?.capabilities?.adminApiManagement === true
|| roles.includes("admin")
|| scopes.includes("api:admin");
return {
connected,
apiKeyRequired,
authenticated: auth?.authenticated === true,
clientId: String(auth?.clientId || source?.clientId || "").trim(),
accountId: String(auth?.accountId || source?.accountId || "").trim(),
accessLevel,
roles,
scopes,
capabilities: {
tarot: tarotCapability,
adminApiManagement: adminApiManagementCapability
}
};
}
function sameConnectionAccess(left, right) {
return JSON.stringify(left) === JSON.stringify(right);
}
function normalizeConnectionSettings(settings) { function normalizeConnectionSettings(settings) {
return { return {
apiBaseUrl: normalizeBaseUrl(settings?.apiBaseUrl), apiBaseUrl: normalizeBaseUrl(settings?.apiBaseUrl),
@@ -90,11 +153,13 @@
stripLegacyCredentialParams(); stripLegacyCredentialParams();
const initialConnectionSettings = readConfiguredConnectionSettings(); const initialConnectionSettings = readConfiguredConnectionSettings();
const initialConnectionAccess = normalizeConnectionAccess(null);
window.TarotAppConfig = { window.TarotAppConfig = {
...(window.TarotAppConfig || {}), ...(window.TarotAppConfig || {}),
apiBaseUrl: initialConnectionSettings.apiBaseUrl, apiBaseUrl: initialConnectionSettings.apiBaseUrl,
apiKey: initialConnectionSettings.apiKey, apiKey: initialConnectionSettings.apiKey,
connectionAccess: initialConnectionAccess,
getApiBaseUrl() { getApiBaseUrl() {
return normalizeBaseUrl(this.apiBaseUrl); return normalizeBaseUrl(this.apiBaseUrl);
}, },
@@ -110,6 +175,31 @@
apiKey: this.getApiKey() apiKey: this.getApiKey()
}; };
}, },
getConnectionAccess() {
return normalizeConnectionAccess(this.connectionAccess || defaultConnectionAccess);
},
hasTarotAccess() {
return this.getConnectionAccess().capabilities.tarot === true;
},
hasAdminApiManagementAccess() {
return this.getConnectionAccess().capabilities.adminApiManagement === true;
},
updateConnectionAccess(nextAccess = null) {
const previous = this.getConnectionAccess();
const current = normalizeConnectionAccess(nextAccess);
this.connectionAccess = current;
if (!sameConnectionAccess(previous, current)) {
document.dispatchEvent(new CustomEvent("connection:access-updated", {
detail: {
previous,
current: { ...current, roles: [...current.roles], scopes: [...current.scopes], capabilities: { ...current.capabilities } }
}
}));
}
return current;
},
updateConnectionSettings(nextSettings = {}) { updateConnectionSettings(nextSettings = {}) {
const previous = this.getConnectionSettings(); const previous = this.getConnectionSettings();
const current = normalizeConnectionSettings({ const current = normalizeConnectionSettings({
+11 -3
View File
@@ -11,6 +11,7 @@
calendarVisualsUi: null, calendarVisualsUi: null,
homeUi: null, homeUi: null,
onStatus: null, onStatus: null,
hasTarotAccess: () => false,
services: {}, services: {},
ensure: {} ensure: {}
}; };
@@ -112,12 +113,15 @@
renderInProgress = true; renderInProgress = true;
try { try {
const tarotAccessEnabled = config.hasTarotAccess?.() === true;
currentGeo = parseGeoInput(); currentGeo = parseGeoInput();
config.homeUi?.syncNowPanelTheme?.(new Date()); config.homeUi?.syncNowPanelTheme?.(new Date());
config.homeUi?.syncNowSkyBackground?.(currentGeo); config.homeUi?.syncNowSkyBackground?.(currentGeo);
if (!referenceData || !magickDataset) { if (!referenceData || !magickDataset) {
setStatus("Loading planetary, sign and decan tarot correspondences..."); setStatus(tarotAccessEnabled
? "Loading planetary, sign and decan tarot correspondences..."
: "Loading planetary, sign, and calendar correspondences...");
const [loadedReference, loadedMagick] = await Promise.all([ const [loadedReference, loadedMagick] = await Promise.all([
referenceData ? Promise.resolve(referenceData) : config.services.loadReferenceData?.(), referenceData ? Promise.resolve(referenceData) : config.services.loadReferenceData?.(),
magickDataset magickDataset
@@ -129,7 +133,9 @@
magickDataset = loadedMagick; magickDataset = loadedMagick;
} }
config.ensure.ensureTarotSection?.(referenceData, magickDataset); if (tarotAccessEnabled) {
config.ensure.ensureTarotSection?.(referenceData, magickDataset);
}
config.ensure.ensurePlanetSection?.(referenceData, magickDataset); config.ensure.ensurePlanetSection?.(referenceData, magickDataset);
config.ensure.ensureCyclesSection?.(referenceData); config.ensure.ensureCyclesSection?.(referenceData);
config.ensure.ensureIChingSection?.(referenceData); config.ensure.ensureIChingSection?.(referenceData);
@@ -152,7 +158,9 @@
config.calendarVisualsUi?.updateMonthStrip?.(); config.calendarVisualsUi?.updateMonthStrip?.();
}); });
setStatus(`Rendered ${events.length} planetary + tarot events for lat ${currentGeo.latitude}, lng ${currentGeo.longitude}.`); setStatus(tarotAccessEnabled
? `Rendered ${events.length} planetary + tarot events for lat ${currentGeo.latitude}, lng ${currentGeo.longitude}.`
: `Rendered ${events.length} planetary events for lat ${currentGeo.latitude}, lng ${currentGeo.longitude}.`);
startNowTicker(); startNowTicker();
} catch (error) { } catch (error) {
setStatus(error?.message || "Failed to render calendar."); setStatus(error?.message || "Failed to render calendar.");
+25 -5
View File
@@ -168,6 +168,12 @@
.trim(); .trim();
} }
function hasAdminCapability(auth) {
const roles = Array.isArray(auth?.roles) ? auth.roles.map((value) => String(value || "").trim()) : [];
const scopes = Array.isArray(auth?.scopes) ? auth.scopes.map((value) => String(value || "").trim()) : [];
return roles.includes("admin") || scopes.includes("api:admin");
}
function isApiEnabled() { function isApiEnabled() {
return Boolean(getApiBaseUrl()); return Boolean(getApiBaseUrl());
} }
@@ -614,7 +620,7 @@
} }
const health = await healthResponse.json().catch(() => null); const health = await healthResponse.json().catch(() => null);
const protectedResponse = await fetch(buildApiUrl("/api/v1/decks/options", {}, resolvedConnection), requestOptions); const protectedResponse = await fetch(buildApiUrl("/api/v1/astrology/planets", {}, resolvedConnection), requestOptions);
if (protectedResponse.status === 401 || protectedResponse.status === 403) { if (protectedResponse.status === 401 || protectedResponse.status === 403) {
return { return {
@@ -630,18 +636,32 @@
return { return {
ok: false, ok: false,
reason: "protected-route-failed", reason: "protected-route-failed",
message: `The API responded with ${protectedResponse.status} when loading protected data.` message: `The API responded with ${protectedResponse.status} when loading connected data.`
}; };
} }
const decksPayload = await protectedResponse.json().catch(() => null); let tarotAvailable = false;
let deckCount = null;
const tarotResponse = await fetch(buildApiUrl("/api/v1/decks/options", {}, resolvedConnection), requestOptions);
if (tarotResponse.ok) {
const decksPayload = await tarotResponse.json().catch(() => null);
deckCount = Array.isArray(decksPayload?.decks) ? decksPayload.decks.length : null;
tarotAvailable = true;
} else if (tarotResponse.status !== 401 && tarotResponse.status !== 403) {
deckCount = null;
}
return { return {
ok: true, ok: true,
reason: "connected", reason: "connected",
message: "Connected.", message: tarotAvailable ? "Connected." : "Connected. Tarot features are unavailable for this API key.",
health, health,
auth: health?.auth || null, auth: health?.auth || null,
deckCount: Array.isArray(decksPayload?.decks) ? decksPayload.decks.length : null deckCount,
capabilities: {
tarot: tarotAvailable,
adminApiManagement: hasAdminCapability(health?.auth)
}
}; };
} catch (_error) { } catch (_error) {
return { return {
+13 -7
View File
@@ -29,6 +29,10 @@
return `${String(normalized).split("").join(" + ")} = ${digitalRoot}`; return `${String(normalized).split("").join(" + ")} = ${digitalRoot}`;
} }
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function renderPositionDigitalRootCard(letter, alphabet, context, orderLabel) { function renderPositionDigitalRootCard(letter, alphabet, context, orderLabel) {
const index = Number(letter?.index); const index = Number(letter?.index);
if (!Number.isFinite(index)) { if (!Number.isFinite(index)) {
@@ -145,6 +149,10 @@
} }
function renderTarotValue(value, context) { function renderTarotValue(value, context) {
if (!hasTarotAccess()) {
return "";
}
const label = String(value || "").trim(); const label = String(value || "").trim();
if (!label) { if (!label) {
return "—"; return "—";
@@ -408,11 +416,9 @@
} }
if (letter.kabbalahPathNumber) { if (letter.kabbalahPathNumber) {
const tarotPart = letter.tarot const tarotAccessEnabled = hasTarotAccess();
? `<dt>Tarot Card</dt><dd>${letter.tarot.card} (Trump ${letter.tarot.trumpNumber})</dd>`
: "";
const kabBtn = context.inlineNavBtn(`Path ${letter.kabbalahPathNumber}`, "tarot:view-kab-path", { "path-number": letter.kabbalahPathNumber }); const kabBtn = context.inlineNavBtn(`Path ${letter.kabbalahPathNumber}`, "tarot:view-kab-path", { "path-number": letter.kabbalahPathNumber });
const tarotBtn = letter.tarot const tarotBtn = tarotAccessEnabled && letter.tarot
? context.inlineNavBtn(letter.tarot.card, "kab:view-trump", { "trump-number": letter.tarot.trumpNumber }) ? context.inlineNavBtn(letter.tarot.card, "kab:view-trump", { "trump-number": letter.tarot.trumpNumber })
: ""; : "";
const cubePlacement = context.getCubePlacementForHebrewLetter(letter.hebrewLetterId, letter.kabbalahPathNumber); const cubePlacement = context.getCubePlacementForHebrewLetter(letter.hebrewLetterId, letter.kabbalahPathNumber);
@@ -420,10 +426,10 @@
"hebrew-letter-id": letter.hebrewLetterId, "hebrew-letter-id": letter.hebrewLetterId,
"path-no": letter.kabbalahPathNumber "path-no": letter.kabbalahPathNumber
}); });
sections.push(context.card("Kabbalah & Tarot", ` sections.push(context.card(tarotAccessEnabled ? "Kabbalah & Tarot" : "Kabbalah", `
<dl class="alpha-dl"> <dl class="alpha-dl">
<dt>Path Number</dt><dd>${kabBtn}</dd> <dt>Path Number</dt><dd>${kabBtn}</dd>
${letter.tarot ? `<dt>Tarot Card</dt><dd>${tarotBtn} (Trump ${letter.tarot.trumpNumber})</dd>` : ""} ${tarotAccessEnabled && letter.tarot ? `<dt>Tarot Card</dt><dd>${tarotBtn} (Trump ${letter.tarot.trumpNumber})</dd>` : ""}
</dl> </dl>
${cubeBtn ? `<div class="planet-text">Cube placement ${cubeBtn}</div>` : ""} ${cubeBtn ? `<div class="planet-text">Cube placement ${cubeBtn}</div>` : ""}
`)); `));
@@ -580,7 +586,7 @@
<dt>English Letters</dt><dd>${englishRefs.join(" / ") || "—"}</dd> <dt>English Letters</dt><dd>${englishRefs.join(" / ") || "—"}</dd>
<dt>Transliteration</dt><dd>${letter.transliteration || "—"}</dd> <dt>Transliteration</dt><dd>${letter.transliteration || "—"}</dd>
<dt>Element / Planet</dt><dd>${renderElementOrPlanetValue(letter.elementOrPlanet, context)}</dd> <dt>Element / Planet</dt><dd>${renderElementOrPlanetValue(letter.elementOrPlanet, context)}</dd>
<dt>Tarot</dt><dd>${renderTarotValue(letter.tarot, context)}</dd> ${hasTarotAccess() ? `<dt>Tarot</dt><dd>${renderTarotValue(letter.tarot, context)}</dd>` : ""}
<dt>Numerology</dt><dd>${letter.numerology || "—"}</dd> <dt>Numerology</dt><dd>${letter.numerology || "—"}</dd>
<dt>Glyph Source</dt><dd>API asset: img/enochian (sourced from dCode set)</dd> <dt>Glyph Source</dt><dd>API asset: img/enochian (sourced from dCode set)</dd>
<dt>Position</dt><dd>#${letter.index} of 21</dd> <dt>Position</dt><dd>#${letter.index} of 21</dd>
+12
View File
@@ -1,6 +1,10 @@
(function () { (function () {
"use strict"; "use strict";
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function findSignIdByAstrologyName(name, context) { function findSignIdByAstrologyName(name, context) {
const { api, getState } = context; const { api, getState } = context;
const token = api.normalizeCalendarText(name); const token = api.normalizeCalendarText(name);
@@ -140,6 +144,10 @@
} }
function renderMajorArcanaCard(context) { function renderMajorArcanaCard(context) {
if (!hasTarotAccess()) {
return "";
}
const { month, api } = context; const { month, api } = context;
const selectedDay = api.getSelectedDayFilterContext(month); const selectedDay = api.getSelectedDayFilterContext(month);
const allRows = buildMajorArcanaRowsForMonth(context); const allRows = buildMajorArcanaRowsForMonth(context);
@@ -191,6 +199,10 @@
} }
function renderDecanTarotCard(context) { function renderDecanTarotCard(context) {
if (!hasTarotAccess()) {
return "";
}
const { month, api } = context; const { month, api } = context;
const selectedDay = api.getSelectedDayFilterContext(month); const selectedDay = api.getSelectedDayFilterContext(month);
const allRows = api.buildDecanTarotRowsForMonth(month); const allRows = api.buildDecanTarotRowsForMonth(month);
+5 -1
View File
@@ -52,6 +52,10 @@
Object.assign(api, config || {}); Object.assign(api, config || {});
} }
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function getState() { function getState() {
return api.getState?.() || {}; return api.getState?.() || {};
} }
@@ -158,7 +162,7 @@
} }
} }
if (associations.tarotCard) { if (associations.tarotCard && hasTarotAccess()) {
const explicitTrumpNumber = Number(associations.tarotTrumpNumber); const explicitTrumpNumber = Number(associations.tarotTrumpNumber);
const tarotTrumpNumber = Number.isFinite(explicitTrumpNumber) ? explicitTrumpNumber : null; const tarotTrumpNumber = Number.isFinite(explicitTrumpNumber) ? explicitTrumpNumber : null;
const tarotLabel = api.getDisplayTarotName(associations.tarotCard, tarotTrumpNumber); const tarotLabel = api.getDisplayTarotName(associations.tarotCard, tarotTrumpNumber);
+10 -6
View File
@@ -104,6 +104,10 @@
return list; return list;
} }
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function createAstrologyValue(astrology, context) { function createAstrologyValue(astrology, context) {
const type = toDisplayText(astrology?.type).toLowerCase(); const type = toDisplayText(astrology?.type).toLowerCase();
const name = toDisplayText(astrology?.name); const name = toDisplayText(astrology?.name);
@@ -150,7 +154,7 @@
const tarotCard = toDisplayText(pathEntry?.tarot?.card); const tarotCard = toDisplayText(pathEntry?.tarot?.card);
const tarotTrumpNumber = context.toFiniteNumber(pathEntry?.tarot?.trumpNumber); const tarotTrumpNumber = context.toFiniteNumber(pathEntry?.tarot?.trumpNumber);
if (tarotCard || tarotTrumpNumber != null) { if (hasTarotAccess() && (tarotCard || tarotTrumpNumber != null)) {
rows.push({ rows.push({
label: "Tarot", label: "Tarot",
value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", { value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", {
@@ -223,7 +227,7 @@
{ label: "Element", value: createElementValue(center?.element, context) || center?.element } { label: "Element", value: createElementValue(center?.element, context) || center?.element }
]; ];
if (centerTarotCard || centerTrumpNo != null) { if (hasTarotAccess() && (centerTarotCard || centerTrumpNo != null)) {
summaryRows.push({ summaryRows.push({
label: "Tarot", label: "Tarot",
value: createInlineEventLink(centerTarotCard || `Trump ${centerTrumpNo}`, "nav:tarot-trump", { value: createInlineEventLink(centerTarotCard || `Trump ${centerTrumpNo}`, "nav:tarot-trump", {
@@ -327,7 +331,7 @@
} }
]; ];
if (tarotCard || tarotTrumpNumber != null) { if (hasTarotAccess() && (tarotCard || tarotTrumpNumber != null)) {
connectorRows.push({ connectorRows.push({
label: "Tarot", label: "Tarot",
value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", { value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", {
@@ -428,7 +432,7 @@
} }
]; ];
if (tarotCard || tarotTrumpNumber != null) { if (hasTarotAccess() && (tarotCard || tarotTrumpNumber != null)) {
edgeRows.push({ edgeRows.push({
label: "Tarot", label: "Tarot",
value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", { value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", {
@@ -562,7 +566,7 @@
} else { } else {
const directTarotCard = toDisplayText(wallAssociations?.tarotCard || wall?.tarotCard); const directTarotCard = toDisplayText(wallAssociations?.tarotCard || wall?.tarotCard);
const directTrumpNumber = toFiniteNumber(wallAssociations?.tarotTrumpNumber); const directTrumpNumber = toFiniteNumber(wallAssociations?.tarotTrumpNumber);
if (directTarotCard || directTrumpNumber != null) { if (hasTarotAccess() && (directTarotCard || directTrumpNumber != null)) {
wallRows.push({ wallRows.push({
label: "Tarot", label: "Tarot",
value: createInlineEventLink(directTarotCard || `Trump ${directTrumpNumber}`, "nav:tarot-trump", { value: createInlineEventLink(directTarotCard || `Trump ${directTrumpNumber}`, "nav:tarot-trump", {
@@ -734,7 +738,7 @@
} }
]; ];
if (tarotCard || tarotTrumpNumber != null) { if (hasTarotAccess() && (tarotCard || tarotTrumpNumber != null)) {
edgeRows.push({ edgeRows.push({
label: "Tarot", label: "Tarot",
value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", { value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", {
+91 -82
View File
@@ -42,6 +42,10 @@
earth: "Disks" earth: "Disks"
}; };
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
const SMALL_CARD_GROUPS = [ const SMALL_CARD_GROUPS = [
{ label: "24", modality: "Cardinal", numbers: [2, 3, 4] }, { label: "24", modality: "Cardinal", numbers: [2, 3, 4] },
{ label: "57", modality: "Fixed", numbers: [5, 6, 7] }, { label: "57", modality: "Fixed", numbers: [5, 6, 7] },
@@ -324,106 +328,111 @@
detailsCard.append(detailsTitle, detailsList); detailsCard.append(detailsTitle, detailsList);
const tarotCard = document.createElement("div"); const detailCards = [detailsCard];
tarotCard.className = "planet-meta-card";
const tarotTitle = document.createElement("strong"); if (hasTarotAccess()) {
tarotTitle.textContent = "Tarot Correspondence"; const tarotCard = document.createElement("div");
tarotCard.className = "planet-meta-card";
const tarotParts = []; const tarotTitle = document.createElement("strong");
if (entry.aceCardName) { tarotTitle.textContent = "Tarot Correspondence";
tarotParts.push(
"Ace: ",
createInlineButton(entry.aceCardName, () => {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
detail: { cardName: entry.aceCardName }
}));
})
);
}
if (entry.courtRank) {
if (tarotParts.length) {
tarotParts.push(" · ");
}
tarotParts.push(`Court Rank: ${entry.courtRank} (all suits)`);
}
const tarotText = tarotParts.length const tarotParts = [];
? createInlineParagraph(tarotParts) if (entry.aceCardName) {
: document.createTextNode("--"); tarotParts.push(
"Ace: ",
tarotCard.append(tarotTitle, tarotText); createInlineButton(entry.aceCardName, () => {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
if (entry.courtCardNames.length) { detail: { cardName: entry.aceCardName }
const courtParts = ["Court cards: "];
entry.courtCardNames.forEach((cardName, index) => {
if (index > 0) {
courtParts.push(", ");
}
courtParts.push(createInlineButton(cardName, () => {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
detail: { cardName }
}));
}));
});
tarotCard.appendChild(createInlineParagraph(courtParts));
}
const smallCardCard = document.createElement("div");
smallCardCard.className = "planet-meta-card";
const smallCardTitle = document.createElement("strong");
smallCardTitle.textContent = "Small Card Sign Types";
smallCardCard.appendChild(smallCardTitle);
const smallCardStack = document.createElement("div");
smallCardStack.className = "cal-item-stack";
(entry.smallCardGroups || []).forEach((group) => {
const row = document.createElement("div");
row.className = "cal-item-row";
const head = document.createElement("div");
head.className = "cal-item-head";
head.innerHTML = `
<span class="cal-item-name">${group.rangeLabel} · ${group.modality}</span>
<span class="planet-list-meta">${group.signName || "--"}</span>
`;
row.appendChild(head);
if (group.signId) {
row.appendChild(createInlineParagraph([
"Sign: ",
createInlineButton(group.signName, () => {
document.dispatchEvent(new CustomEvent("nav:zodiac", {
detail: { signId: group.signId }
})); }));
}) })
])); );
}
if (entry.courtRank) {
if (tarotParts.length) {
tarotParts.push(" · ");
}
tarotParts.push(`Court Rank: ${entry.courtRank} (all suits)`);
} }
if ((group.cardNames || []).length) { const tarotText = tarotParts.length
const cardParts = ["Cards: "]; ? createInlineParagraph(tarotParts)
group.cardNames.forEach((cardName, index) => { : document.createTextNode("--");
tarotCard.append(tarotTitle, tarotText);
if (entry.courtCardNames.length) {
const courtParts = ["Court cards: "];
entry.courtCardNames.forEach((cardName, index) => {
if (index > 0) { if (index > 0) {
cardParts.push(", "); courtParts.push(", ");
} }
cardParts.push(createInlineButton(cardName, () => { courtParts.push(createInlineButton(cardName, () => {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", { document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
detail: { cardName } detail: { cardName }
})); }));
})); }));
}); });
row.appendChild(createInlineParagraph(cardParts));
tarotCard.appendChild(createInlineParagraph(courtParts));
} }
smallCardStack.appendChild(row); const smallCardCard = document.createElement("div");
}); smallCardCard.className = "planet-meta-card";
smallCardCard.appendChild(smallCardStack); const smallCardTitle = document.createElement("strong");
smallCardTitle.textContent = "Small Card Sign Types";
smallCardCard.appendChild(smallCardTitle);
grid.append(detailsCard, tarotCard, smallCardCard); const smallCardStack = document.createElement("div");
smallCardStack.className = "cal-item-stack";
(entry.smallCardGroups || []).forEach((group) => {
const row = document.createElement("div");
row.className = "cal-item-row";
const head = document.createElement("div");
head.className = "cal-item-head";
head.innerHTML = `
<span class="cal-item-name">${group.rangeLabel} · ${group.modality}</span>
<span class="planet-list-meta">${group.signName || "--"}</span>
`;
row.appendChild(head);
if (group.signId) {
row.appendChild(createInlineParagraph([
"Sign: ",
createInlineButton(group.signName, () => {
document.dispatchEvent(new CustomEvent("nav:zodiac", {
detail: { signId: group.signId }
}));
})
]));
}
if ((group.cardNames || []).length) {
const cardParts = ["Cards: "];
group.cardNames.forEach((cardName, index) => {
if (index > 0) {
cardParts.push(", ");
}
cardParts.push(createInlineButton(cardName, () => {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
detail: { cardName }
}));
}));
});
row.appendChild(createInlineParagraph(cardParts));
}
smallCardStack.appendChild(row);
});
smallCardCard.appendChild(smallCardStack);
detailCards.push(tarotCard, smallCardCard);
}
grid.append(...detailCards);
elements.detailBodyEl.appendChild(grid); elements.detailBodyEl.appendChild(grid);
} }
+9 -2
View File
@@ -30,6 +30,10 @@
lettersById: new Map() lettersById: new Map()
}; };
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function getElements() { function getElements() {
return { return {
listEl: document.getElementById("enochian-list"), listEl: document.getElementById("enochian-list"),
@@ -306,10 +310,13 @@
const detailRows = [ const detailRows = [
["Pronunciation", letterData.pronounciation], ["Pronunciation", letterData.pronounciation],
["Planet / Element", letterData["planet/element"]], ["Planet / Element", letterData["planet/element"]],
["Tarot", letterData.tarot],
["Gematria", letterData.gematria] ["Gematria", letterData.gematria]
]; ];
if (hasTarotAccess()) {
detailRows.splice(2, 0, ["Tarot", letterData.tarot]);
}
detailRows.forEach(([label, value]) => { detailRows.forEach(([label, value]) => {
if (value === undefined || value === null || String(value).trim() === "") { if (value === undefined || value === null || String(value).trim() === "") {
return; return;
@@ -324,7 +331,7 @@
navRow.className = "enoch-letter-row"; navRow.className = "enoch-letter-row";
const tarotCardName = resolveTarotCardName(letterData.tarot); const tarotCardName = resolveTarotCardName(letterData.tarot);
if (tarotCardName) { if (hasTarotAccess() && tarotCardName) {
const tarotBtn = document.createElement("button"); const tarotBtn = document.createElement("button");
tarotBtn.type = "button"; tarotBtn.type = "button";
tarotBtn.className = "enoch-nav-btn"; tarotBtn.className = "enoch-nav-btn";
+5 -1
View File
@@ -73,6 +73,10 @@
return current; return current;
} }
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function buildInlineNavButton(label, nav, attrs = {}) { function buildInlineNavButton(label, nav, attrs = {}) {
const dataAttrs = Object.entries(attrs) const dataAttrs = Object.entries(attrs)
.map(([key, value]) => `data-${key}="${value}"`) .map(([key, value]) => `data-${key}="${value}"`)
@@ -109,7 +113,7 @@
} }
} }
if (associations.tarotCard) { if (associations.tarotCard && hasTarotAccess()) {
const trumpNumber = resolveTarotTrumpNumber(associations.tarotCard); const trumpNumber = resolveTarotTrumpNumber(associations.tarotCard);
const explicitTrumpNumber = Number(associations.tarotTrumpNumber); const explicitTrumpNumber = Number(associations.tarotTrumpNumber);
const tarotTrumpNumber = Number.isFinite(explicitTrumpNumber) ? explicitTrumpNumber : trumpNumber; const tarotTrumpNumber = Number.isFinite(explicitTrumpNumber) ? explicitTrumpNumber : trumpNumber;
+15 -1
View File
@@ -20,6 +20,10 @@
selectedNumber: null selectedNumber: null
}; };
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
const ICHING_PLANET_BY_PLANET_ID = { const ICHING_PLANET_BY_PLANET_ID = {
sol: "Sun", sol: "Sun",
luna: "Moon", luna: "Moon",
@@ -235,7 +239,8 @@
elements.ichingDetailPlanetEl.textContent = "--"; elements.ichingDetailPlanetEl.textContent = "--";
} }
if (elements.ichingDetailTarotEl) { if (elements.ichingDetailTarotEl) {
elements.ichingDetailTarotEl.textContent = "--"; elements.ichingDetailTarotEl.hidden = !hasTarotAccess();
elements.ichingDetailTarotEl.textContent = hasTarotAccess() ? "--" : "";
} }
if (elements.ichingDetailCalendarEl) { if (elements.ichingDetailCalendarEl) {
clearChildren(elements.ichingDetailCalendarEl); clearChildren(elements.ichingDetailCalendarEl);
@@ -327,6 +332,15 @@
return; return;
} }
if (!hasTarotAccess()) {
clearChildren(elements.ichingDetailTarotEl);
elements.ichingDetailTarotEl.hidden = true;
elements.ichingDetailTarotEl.textContent = "";
return;
}
elements.ichingDetailTarotEl.hidden = false;
clearChildren(elements.ichingDetailTarotEl); clearChildren(elements.ichingDetailTarotEl);
const upperKey = normalizeSearchValue(entry?.upperTrigram); const upperKey = normalizeSearchValue(entry?.upperTrigram);
+37 -23
View File
@@ -26,6 +26,10 @@
const MINOR_SUITS = ["Wands", "Cups", "Swords", "Disks"]; const MINOR_SUITS = ["Wands", "Cups", "Swords", "Disks"];
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
const DEFAULT_FOUR_QABALISTIC_WORLD_LAYERS = [ const DEFAULT_FOUR_QABALISTIC_WORLD_LAYERS = [
{ {
slot: "Yod", slot: "Yod",
@@ -167,6 +171,10 @@
} }
function buildTarotAttributionCard(attribution) { function buildTarotAttributionCard(attribution) {
if (!hasTarotAccess()) {
return null;
}
const minorCards = buildMinorTarotNames(attribution); const minorCards = buildMinorTarotNames(attribution);
if (minorCards.length) { if (minorCards.length) {
const parts = []; const parts = [];
@@ -485,7 +493,10 @@
elements.detailBodyEl.innerHTML = ""; elements.detailBodyEl.innerHTML = "";
elements.detailBodyEl.appendChild(buildPlanetLuminaryCard(seph.planet, context)); elements.detailBodyEl.appendChild(buildPlanetLuminaryCard(seph.planet, context));
elements.detailBodyEl.appendChild(metaCard("Intelligence", seph.intelligence)); elements.detailBodyEl.appendChild(metaCard("Intelligence", seph.intelligence));
elements.detailBodyEl.appendChild(buildTarotAttributionCard(seph.tarot)); const tarotAttributionCard = buildTarotAttributionCard(seph.tarot);
if (tarotAttributionCard) {
elements.detailBodyEl.appendChild(tarotAttributionCard);
}
if (seph.description) { if (seph.description) {
elements.detailBodyEl.appendChild( elements.detailBodyEl.appendChild(
@@ -500,7 +511,7 @@
const card = document.createElement("div"); const card = document.createElement("div");
card.className = "planet-meta-card kab-wide-card"; card.className = "planet-meta-card kab-wide-card";
const chips = connected.map((entry) => const chips = connected.map((entry) =>
`<span class="kab-chip" data-path="${entry.pathNumber}" role="button" tabindex="0" title="Path ${entry.pathNumber}: ${entry.tarot?.card || ""}">` `<span class="kab-chip" data-path="${entry.pathNumber}" role="button" tabindex="0" title="${hasTarotAccess() ? `Path ${entry.pathNumber}: ${entry.tarot?.card || ""}` : `Path ${entry.pathNumber}`}">`
+ `${entry.hebrewLetter?.char || ""} <span class="kab-chip-sub">${entry.pathNumber}</span>` + `${entry.hebrewLetter?.char || ""} <span class="kab-chip-sub">${entry.pathNumber}</span>`
+ `</span>` + `</span>`
).join(""); ).join("");
@@ -533,39 +544,42 @@
const fromName = tree.sephiroth.find((entry) => entry.number === path.connects.from)?.name || path.connects.from; const fromName = tree.sephiroth.find((entry) => entry.number === path.connects.from)?.name || path.connects.from;
const toName = tree.sephiroth.find((entry) => entry.number === path.connects.to)?.name || path.connects.to; const toName = tree.sephiroth.find((entry) => entry.number === path.connects.to)?.name || path.connects.to;
const astro = path.astrology ? `${path.astrology.name} (${path.astrology.type})` : "—"; const astro = path.astrology ? `${path.astrology.name} (${path.astrology.type})` : "—";
const tarotStr = path.tarot?.card const tarotAccessEnabled = hasTarotAccess();
const tarotStr = tarotAccessEnabled && path.tarot?.card
? `${path.tarot.card}${path.tarot.trumpNumber != null ? " · Trump " + path.tarot.trumpNumber : ""}` ? `${path.tarot.card}${path.tarot.trumpNumber != null ? " · Trump " + path.tarot.trumpNumber : ""}`
: "—"; : "—";
elements.detailNameEl.textContent = elements.detailNameEl.textContent =
`Path ${path.pathNumber} · ${letter.char || ""} ${letter.transliteration || ""}`; `Path ${path.pathNumber} · ${letter.char || ""} ${letter.transliteration || ""}`;
elements.detailSubEl.textContent = [path.tarot?.card, astro].filter(Boolean).join(" · "); elements.detailSubEl.textContent = [tarotAccessEnabled ? path.tarot?.card : "", astro].filter(Boolean).join(" · ");
elements.detailBodyEl.innerHTML = ""; elements.detailBodyEl.innerHTML = "";
elements.detailBodyEl.appendChild(buildConnectsCard(path, fromName, toName)); elements.detailBodyEl.appendChild(buildConnectsCard(path, fromName, toName));
elements.detailBodyEl.appendChild(buildHebrewLetterCard(letter, context)); elements.detailBodyEl.appendChild(buildHebrewLetterCard(letter, context));
elements.detailBodyEl.appendChild(buildAstrologyCard(path.astrology, context)); elements.detailBodyEl.appendChild(buildAstrologyCard(path.astrology, context));
const tarotMetaCard = document.createElement("div"); if (tarotAccessEnabled) {
tarotMetaCard.className = "planet-meta-card"; const tarotMetaCard = document.createElement("div");
const tarotLabel = document.createElement("strong"); tarotMetaCard.className = "planet-meta-card";
tarotLabel.textContent = "Tarot"; const tarotLabel = document.createElement("strong");
tarotMetaCard.appendChild(tarotLabel); tarotLabel.textContent = "Tarot";
if (path.tarot?.card && path.tarot.trumpNumber != null) { tarotMetaCard.appendChild(tarotLabel);
const tarotBtn = createInlineEventLink( if (path.tarot?.card && path.tarot.trumpNumber != null) {
`${path.tarot.card} · Trump ${path.tarot.trumpNumber}`, const tarotBtn = createInlineEventLink(
"kab:view-trump", `${path.tarot.card} · Trump ${path.tarot.trumpNumber}`,
{ trumpNumber: path.tarot.trumpNumber } "kab:view-trump",
); { trumpNumber: path.tarot.trumpNumber }
tarotBtn.title = "Open in Tarot section"; );
tarotMetaCard.appendChild(tarotBtn); tarotBtn.title = "Open in Tarot section";
} else { tarotMetaCard.appendChild(tarotBtn);
const tarotP = document.createElement("p"); } else {
tarotP.className = "planet-text"; const tarotP = document.createElement("p");
tarotP.textContent = tarotStr || "—"; tarotP.className = "planet-text";
tarotMetaCard.appendChild(tarotP); tarotP.textContent = tarotStr || "—";
tarotMetaCard.appendChild(tarotP);
}
elements.detailBodyEl.appendChild(tarotMetaCard);
} }
elements.detailBodyEl.appendChild(tarotMetaCard);
elements.detailBodyEl.appendChild(metaCard("Intelligence", path.intelligence)); elements.detailBodyEl.appendChild(metaCard("Intelligence", path.intelligence));
elements.detailBodyEl.appendChild(metaCard("Pillar", path.pillar)); elements.detailBodyEl.appendChild(metaCard("Pillar", path.pillar));
+15 -2
View File
@@ -93,6 +93,10 @@
} }
async function prepareTarotBrowseDetailView() { async function prepareTarotBrowseDetailView() {
if (window.TarotAppConfig?.hasTarotAccess?.() !== true) {
return false;
}
const ensure = config.ensure || {}; const ensure = config.ensure || {};
const referenceData = getReferenceData(); const referenceData = getReferenceData();
const magickDataset = getMagickDataset(); const magickDataset = getMagickDataset();
@@ -113,6 +117,7 @@
}); });
showSectionDetailOnly("tarot"); showSectionDetailOnly("tarot");
return true;
} }
function bindClick(element, handler) { function bindClick(element, handler) {
@@ -491,7 +496,11 @@
}); });
document.addEventListener("nav:tarot-trump", async (event) => { document.addEventListener("nav:tarot-trump", async (event) => {
await prepareTarotBrowseDetailView(); const tarotReady = await prepareTarotBrowseDetailView();
if (!tarotReady) {
return;
}
const { trumpNumber, cardName } = event?.detail || {}; const { trumpNumber, cardName } = event?.detail || {};
if (trumpNumber != null) { if (trumpNumber != null) {
@@ -502,7 +511,11 @@
}); });
document.addEventListener("kab:view-trump", async (event) => { document.addEventListener("kab:view-trump", async (event) => {
await prepareTarotBrowseDetailView(); const tarotReady = await prepareTarotBrowseDetailView();
if (!tarotReady) {
return;
}
const trumpNumber = event?.detail?.trumpNumber; const trumpNumber = event?.detail?.trumpNumber;
if (trumpNumber != null) { if (trumpNumber != null) {
+31 -11
View File
@@ -58,14 +58,29 @@
} }
} }
function setNowTarotVisibility(imageEl, labelEl, visible) {
if (imageEl) {
imageEl.hidden = !visible;
}
if (labelEl) {
labelEl.hidden = !visible;
}
}
function applyNowSnapshot(elements, snapshot, timeFormat) { function applyNowSnapshot(elements, snapshot, timeFormat) {
const timestamp = snapshot?.timestamp ? new Date(snapshot.timestamp) : new Date(); const timestamp = snapshot?.timestamp ? new Date(snapshot.timestamp) : new Date();
const dayKey = String(snapshot?.dayKey || getDateKey(timestamp)); const dayKey = String(snapshot?.dayKey || getDateKey(timestamp));
const currentHour = snapshot?.currentHour || null; const currentHour = snapshot?.currentHour || null;
const tarotAccessEnabled = window.TarotAppConfig?.hasTarotAccess?.() === true;
setNowTarotVisibility(elements.nowHourCardEl, elements.nowHourTarotEl, tarotAccessEnabled);
setNowTarotVisibility(elements.nowMoonCardEl, elements.nowMoonTarotEl, tarotAccessEnabled);
setNowTarotVisibility(elements.nowDecanCardEl, elements.nowDecanTarotEl, tarotAccessEnabled);
if (currentHour?.planet) { if (currentHour?.planet) {
elements.nowHourEl.textContent = `${currentHour.planet.symbol} ${currentHour.planet.name}`; elements.nowHourEl.textContent = `${currentHour.planet.symbol} ${currentHour.planet.name}`;
if (elements.nowHourTarotEl) { if (tarotAccessEnabled && elements.nowHourTarotEl) {
const hourCardName = currentHour.planet?.tarot?.majorArcana || ""; const hourCardName = currentHour.planet?.tarot?.majorArcana || "";
const hourTrumpNumber = currentHour.planet?.tarot?.number; const hourTrumpNumber = currentHour.planet?.tarot?.number;
elements.nowHourTarotEl.textContent = hourCardName elements.nowHourTarotEl.textContent = hourCardName
@@ -84,7 +99,7 @@
nowUiHelpers.setNowCardImage( nowUiHelpers.setNowCardImage(
elements.nowHourCardEl, elements.nowHourCardEl,
currentHour.planet?.tarot?.majorArcana, tarotAccessEnabled ? currentHour.planet?.tarot?.majorArcana : null,
"Current planetary hour card", "Current planetary hour card",
currentHour.planet?.tarot?.number currentHour.planet?.tarot?.number
); );
@@ -92,7 +107,7 @@
elements.nowHourEl.textContent = "--"; elements.nowHourEl.textContent = "--";
elements.nowCountdownEl.textContent = "--"; elements.nowCountdownEl.textContent = "--";
if (elements.nowHourTarotEl) { if (elements.nowHourTarotEl) {
elements.nowHourTarotEl.textContent = "--"; elements.nowHourTarotEl.textContent = tarotAccessEnabled ? "--" : "";
} }
if (elements.nowHourNextEl) { if (elements.nowHourNextEl) {
elements.nowHourNextEl.textContent = "> --"; elements.nowHourNextEl.textContent = "> --";
@@ -106,12 +121,12 @@
elements.nowMoonEl.textContent = moon elements.nowMoonEl.textContent = moon
? `${moon.phase} (${Math.round(illuminationFraction * 100)}%)` ? `${moon.phase} (${Math.round(illuminationFraction * 100)}%)`
: "--"; : "--";
elements.nowMoonTarotEl.textContent = moon elements.nowMoonTarotEl.textContent = tarotAccessEnabled
? nowUiHelpers.getDisplayTarotName(moonTarot, moon?.tarot?.number) ? (moon ? nowUiHelpers.getDisplayTarotName(moonTarot, moon?.tarot?.number) : "--")
: "--"; : "";
nowUiHelpers.setNowCardImage( nowUiHelpers.setNowCardImage(
elements.nowMoonCardEl, elements.nowMoonCardEl,
moon?.tarot?.majorArcana, tarotAccessEnabled ? moon?.tarot?.majorArcana : null,
"Current moon phase card", "Current moon phase card",
moon?.tarot?.number moon?.tarot?.number
); );
@@ -140,12 +155,14 @@
? Number(decanInfo.signDegree).toFixed(1) ? Number(decanInfo.signDegree).toFixed(1)
: "0.0"; : "0.0";
elements.nowDecanEl.textContent = `${decanInfo.sign.symbol} ${decanInfo.sign.name} · ${signMajorName} (${signDegree}°)`; elements.nowDecanEl.textContent = tarotAccessEnabled
? `${decanInfo.sign.symbol} ${decanInfo.sign.name} · ${signMajorName} (${signDegree}°)`
: `${decanInfo.sign.symbol} ${decanInfo.sign.name} (${signDegree}°)`;
if (decanInfo.decan?.tarotMinorArcana) { if (tarotAccessEnabled && decanInfo.decan?.tarotMinorArcana) {
elements.nowDecanTarotEl.textContent = nowUiHelpers.getDisplayTarotName(decanInfo.decan.tarotMinorArcana); elements.nowDecanTarotEl.textContent = nowUiHelpers.getDisplayTarotName(decanInfo.decan.tarotMinorArcana);
nowUiHelpers.setNowCardImage(elements.nowDecanCardEl, decanInfo.decan.tarotMinorArcana, "Current decan card"); nowUiHelpers.setNowCardImage(elements.nowDecanCardEl, decanInfo.decan.tarotMinorArcana, "Current decan card");
} else { } else if (tarotAccessEnabled) {
const signTarotName = decanInfo.sign?.tarot?.majorArcana || "--"; const signTarotName = decanInfo.sign?.tarot?.majorArcana || "--";
elements.nowDecanTarotEl.textContent = signTarotName === "--" elements.nowDecanTarotEl.textContent = signTarotName === "--"
? "--" ? "--"
@@ -156,6 +173,9 @@
"Current decan card", "Current decan card",
decanInfo.sign?.tarot?.number decanInfo.sign?.tarot?.number
); );
} else {
elements.nowDecanTarotEl.textContent = "";
nowUiHelpers.setNowCardImage(elements.nowDecanCardEl, null, "Current decan card");
} }
if (elements.nowDecanCountdownEl) { if (elements.nowDecanCountdownEl) {
@@ -173,7 +193,7 @@
} }
} else { } else {
elements.nowDecanEl.textContent = "--"; elements.nowDecanEl.textContent = "--";
elements.nowDecanTarotEl.textContent = "--"; elements.nowDecanTarotEl.textContent = tarotAccessEnabled ? "--" : "";
nowUiHelpers.setNowCardImage(elements.nowDecanCardEl, null, "Current decan card"); nowUiHelpers.setNowCardImage(elements.nowDecanCardEl, null, "Current decan card");
if (elements.nowDecanCountdownEl) { if (elements.nowDecanCountdownEl) {
elements.nowDecanCountdownEl.textContent = "--"; elements.nowDecanCountdownEl.textContent = "--";
+47 -33
View File
@@ -1,6 +1,10 @@
(function () { (function () {
"use strict"; "use strict";
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function getCalendarMonthLinksForNumber(context, value) { function getCalendarMonthLinksForNumber(context, value) {
const { getReferenceData, normalizeNumberValue, computeDigitalRoot } = context; const { getReferenceData, normalizeNumberValue, computeDigitalRoot } = context;
const referenceData = getReferenceData(); const referenceData = getReferenceData();
@@ -406,6 +410,10 @@
} }
function getTarotCardsForDigitalRoot(context, targetRoot, numberEntry = null) { function getTarotCardsForDigitalRoot(context, targetRoot, numberEntry = null) {
if (!hasTarotAccess()) {
return [];
}
const { getReferenceData, getMagickDataset, ensureTarotSection, computeDigitalRoot } = context; const { getReferenceData, getMagickDataset, ensureTarotSection, computeDigitalRoot } = context;
const referenceData = getReferenceData(); const referenceData = getReferenceData();
const magickDataset = getMagickDataset(); const magickDataset = getMagickDataset();
@@ -558,38 +566,6 @@
alphabetCardEl.append(alphabetHeadingEl, alphabetLinksWrapEl); alphabetCardEl.append(alphabetHeadingEl, alphabetLinksWrapEl);
const tarotCardEl = document.createElement("div");
tarotCardEl.className = "numbers-detail-card";
const tarotHeadingEl = document.createElement("strong");
tarotHeadingEl.textContent = "Tarot Links";
const tarotLinksWrapEl = document.createElement("div");
tarotLinksWrapEl.className = "numbers-links-wrap";
const tarotCards = getTarotCardsForDigitalRoot(context, rootTarget, entry);
if (!tarotCards.length) {
const emptyEl = document.createElement("div");
emptyEl.className = "numbers-detail-text numbers-detail-text--muted";
emptyEl.textContent = "No tarot numeric entries found yet for this root. Add card numbers to map them.";
tarotLinksWrapEl.appendChild(emptyEl);
} else {
tarotCards.forEach((card) => {
const button = document.createElement("button");
button.type = "button";
button.className = "numbers-nav-btn";
button.textContent = `${card.name}`;
button.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
detail: { cardName: card.name }
}));
});
tarotLinksWrapEl.appendChild(button);
});
}
tarotCardEl.append(tarotHeadingEl, tarotLinksWrapEl);
const calendarCardEl = document.createElement("div"); const calendarCardEl = document.createElement("div");
calendarCardEl.className = "numbers-detail-card"; calendarCardEl.className = "numbers-detail-card";
@@ -625,7 +601,45 @@
calendarCardEl.append(calendarHeadingEl, calendarLinksWrapEl); calendarCardEl.append(calendarHeadingEl, calendarLinksWrapEl);
detailBodyEl.append(pairCardEl, kabbalahCardEl, alphabetCardEl, tarotCardEl, calendarCardEl); const detailCards = [pairCardEl, kabbalahCardEl, alphabetCardEl];
if (hasTarotAccess()) {
const tarotCardEl = document.createElement("div");
tarotCardEl.className = "numbers-detail-card";
const tarotHeadingEl = document.createElement("strong");
tarotHeadingEl.textContent = "Tarot Links";
const tarotLinksWrapEl = document.createElement("div");
tarotLinksWrapEl.className = "numbers-links-wrap";
const tarotCards = getTarotCardsForDigitalRoot(context, rootTarget, entry);
if (!tarotCards.length) {
const emptyEl = document.createElement("div");
emptyEl.className = "numbers-detail-text numbers-detail-text--muted";
emptyEl.textContent = "No tarot numeric entries found yet for this root. Add card numbers to map them.";
tarotLinksWrapEl.appendChild(emptyEl);
} else {
tarotCards.forEach((card) => {
const button = document.createElement("button");
button.type = "button";
button.className = "numbers-nav-btn";
button.textContent = `${card.name}`;
button.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
detail: { cardName: card.name }
}));
});
tarotLinksWrapEl.appendChild(button);
});
}
tarotCardEl.append(tarotHeadingEl, tarotLinksWrapEl);
detailCards.push(tarotCardEl);
}
detailCards.push(calendarCardEl);
detailBodyEl.append(...detailCards);
} }
window.NumbersDetailUi = { window.NumbersDetailUi = {
+6 -2
View File
@@ -23,6 +23,10 @@
}; };
let detailNavigator = null; let detailNavigator = null;
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function normalizePlanetToken(value) { function normalizePlanetToken(value) {
return String(value || "") return String(value || "")
.trim() .trim()
@@ -290,7 +294,7 @@
if (!correspondence || typeof correspondence !== "object") { if (!correspondence || typeof correspondence !== "object") {
const fallback = document.createElement("span"); const fallback = document.createElement("span");
fallback.className = "planet-text"; fallback.className = "planet-text";
fallback.textContent = "No tarot/day correspondence in current local dataset."; fallback.textContent = "No day correspondence in current local dataset.";
containerEl.appendChild(fallback); containerEl.appendChild(fallback);
return; return;
} }
@@ -309,7 +313,7 @@
containerEl.appendChild(line); containerEl.appendChild(line);
} }
if (arcana) { if (arcana && hasTarotAccess()) {
const btn = createInlineButton( const btn = createInlineButton(
trumpNo != null ? `${arcanaLabel} · Trump ${trumpNo}` : arcanaLabel, trumpNo != null ? `${arcanaLabel} · Trump ${trumpNo}` : arcanaLabel,
() => { () => {
+5 -1
View File
@@ -38,6 +38,7 @@
let config = { let config = {
elements: {}, elements: {},
ensure: {}, ensure: {},
isSectionAccessible: () => true,
getReferenceData: () => null, getReferenceData: () => null,
getMagickDataset: () => null, getMagickDataset: () => null,
calendarVisualsUi: null, calendarVisualsUi: null,
@@ -82,7 +83,10 @@
} }
function setActiveSection(nextSection) { function setActiveSection(nextSection) {
const normalized = VALID_SECTIONS.has(nextSection) ? nextSection : "home"; const requestedSection = VALID_SECTIONS.has(nextSection) ? nextSection : "home";
const normalized = config.isSectionAccessible?.(requestedSection) === false
? "home"
: requestedSection;
activeSection = normalized; activeSection = normalized;
const elements = config.elements || {}; const elements = config.elements || {};
+56 -7
View File
@@ -181,6 +181,10 @@
}; };
} }
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
function syncConnectionInputs() { function syncConnectionInputs() {
const { apiBaseUrlEl, apiKeyEl } = getElements(); const { apiBaseUrlEl, apiKeyEl } = getElements();
const connectionSettings = getConnectionSettings(); const connectionSettings = getConnectionSettings();
@@ -251,8 +255,9 @@
const roles = normalizeConnectionValues(auth.roles); const roles = normalizeConnectionValues(auth.roles);
const scopes = normalizeConnectionValues(auth.scopes); const scopes = normalizeConnectionValues(auth.scopes);
const authenticated = auth.authenticated === true; const authenticated = auth.authenticated === true;
const tarotAccessEnabled = result?.capabilities?.tarot === true;
const accessValue = authenticated const accessValue = authenticated
? `${String(auth.accessLevel || "premium").trim() || "premium"}${hasAdminCapability(auth) ? " - admin capable" : ""}` ? `${String(auth.accessLevel || "premium").trim() || "premium"}${hasAdminCapability(auth) ? " - admin capable" : ""}${tarotAccessEnabled ? " - tarot enabled" : " - tarot hidden"}`
: (result.health?.apiKeyRequired ? "API key required" : "public"); : (result.health?.apiKeyRequired ? "API key required" : "public");
const permissionsValue = authenticated const permissionsValue = authenticated
? `roles: ${formatConnectionValues(roles)} | scopes: ${formatConnectionValues(scopes)}` ? `roles: ${formatConnectionValues(roles)} | scopes: ${formatConnectionValues(scopes)}`
@@ -399,6 +404,10 @@
} }
function getKnownTarotDeckIds() { function getKnownTarotDeckIds() {
if (!hasTarotAccess()) {
return new Set([String(config.defaultSettings?.tarotDeck || "ceremonial-magick").trim().toLowerCase()]);
}
const knownDeckIds = new Set(); const knownDeckIds = new Set();
const deckOptions = window.TarotCardImages?.getDeckOptions?.(); const deckOptions = window.TarotCardImages?.getDeckOptions?.();
@@ -419,6 +428,10 @@
} }
function getFallbackTarotDeckId() { function getFallbackTarotDeckId() {
if (!hasTarotAccess()) {
return String(config.defaultSettings?.tarotDeck || "ceremonial-magick").trim().toLowerCase();
}
const deckOptions = window.TarotCardImages?.getDeckOptions?.(); const deckOptions = window.TarotCardImages?.getDeckOptions?.();
if (Array.isArray(deckOptions)) { if (Array.isArray(deckOptions)) {
for (let i = 0; i < deckOptions.length; i += 1) { for (let i = 0; i < deckOptions.length; i += 1) {
@@ -433,6 +446,11 @@
} }
function normalizeTarotDeck(value) { function normalizeTarotDeck(value) {
if (!hasTarotAccess()) {
const preservedValue = String(value || "").trim().toLowerCase();
return preservedValue || String(config.defaultSettings?.tarotDeck || "ceremonial-magick").trim().toLowerCase();
}
const normalized = String(value || "").trim().toLowerCase(); const normalized = String(value || "").trim().toLowerCase();
const knownDeckIds = getKnownTarotDeckIds(); const knownDeckIds = getKnownTarotDeckIds();
@@ -571,6 +589,16 @@
return; return;
} }
if (!hasTarotAccess()) {
tarotDeckEl.innerHTML = "";
const hiddenOption = document.createElement("option");
hiddenOption.value = String(config.defaultSettings?.tarotDeck || "ceremonial-magick").trim().toLowerCase();
hiddenOption.textContent = "Tarot deck unavailable for this API key";
tarotDeckEl.appendChild(hiddenOption);
tarotDeckEl.disabled = true;
return;
}
const deckOptions = window.TarotCardImages?.getDeckOptions?.(); const deckOptions = window.TarotCardImages?.getDeckOptions?.();
const previousValue = String(tarotDeckEl.value || "").trim().toLowerCase(); const previousValue = String(tarotDeckEl.value || "").trim().toLowerCase();
tarotDeckEl.innerHTML = ""; tarotDeckEl.innerHTML = "";
@@ -602,6 +630,24 @@
tarotDeckEl.value = normalizeTarotDeck(previousValue); tarotDeckEl.value = normalizeTarotDeck(previousValue);
} }
function syncActiveTarotDeck(deckId) {
if (!hasTarotAccess()) {
return;
}
const normalizedDeckId = normalizeTarotDeck(deckId);
if (window.TarotCardImages?.setActiveDeck) {
window.TarotCardImages.setActiveDeck(normalizedDeckId);
}
const { tarotDeckEl } = getElements();
if (tarotDeckEl) {
tarotDeckEl.value = normalizedDeckId;
}
syncDeckCacheStatus(window.TarotCardImages?.getDeckPreloadStatus?.());
}
function applySettingsToInputs(settings) { function applySettingsToInputs(settings) {
const { latEl, lngEl, timeFormatEl, birthDateEl, tarotDeckEl, stellariumBackgroundEl } = getElements(); const { latEl, lngEl, timeFormatEl, birthDateEl, tarotDeckEl, stellariumBackgroundEl } = getElements();
syncTarotDeckInputOptions(); syncTarotDeckInputOptions();
@@ -619,10 +665,7 @@
stellariumBackgroundEl.checked = normalized.stellariumBackgroundEnabled; stellariumBackgroundEl.checked = normalized.stellariumBackgroundEnabled;
} }
syncStellariumBackgroundAvailability(); syncStellariumBackgroundAvailability();
if (window.TarotCardImages?.setActiveDeck) { syncActiveTarotDeck(normalized.tarotDeck);
window.TarotCardImages.setActiveDeck(normalized.tarotDeck);
}
syncDeckCacheStatus(window.TarotCardImages?.getDeckPreloadStatus?.());
applyExternalSettings(normalized); applyExternalSettings(normalized);
return normalized; return normalized;
} }
@@ -842,13 +885,19 @@
document.addEventListener("connection:updated", () => { document.addEventListener("connection:updated", () => {
syncConnectionInputs(); syncConnectionInputs();
void refreshConnectionSummary(getConnectionSettings());
});
document.addEventListener("connection:access-updated", () => {
syncTarotDeckInputOptions(); syncTarotDeckInputOptions();
syncDeckCacheStatus(window.TarotCardImages?.getDeckPreloadStatus?.()); syncActiveTarotDeck(getElements().tarotDeckEl?.value || loadSavedSettings().tarotDeck);
void refreshConnectionSummary(getConnectionSettings()); void refreshConnectionSummary(getConnectionSettings());
}); });
document.addEventListener("tarot:deck-cache-status", (event) => { document.addEventListener("tarot:deck-cache-status", (event) => {
syncDeckCacheStatus(event?.detail); if (hasTarotAccess()) {
syncDeckCacheStatus(event?.detail);
}
}); });
} }
+16 -3
View File
@@ -37,6 +37,10 @@
cubePlacementBySignId: new Map() cubePlacementBySignId: new Map()
}; };
function hasTarotAccess() {
return window.TarotAppConfig?.hasTarotAccess?.() === true;
}
// ── Elements ────────────────────────────────────────────────────────── // ── Elements ──────────────────────────────────────────────────────────
function getElements() { function getElements() {
return { return {
@@ -184,12 +188,14 @@
} }
// ── Kabbalah Path + Trump ───────────────────────────────────────── // ── Kabbalah Path + Trump ─────────────────────────────────────────
const tarotAccessEnabled = hasTarotAccess();
if (kabPath) { if (kabPath) {
const hl = kabPath.hebrewLetter || {}; const hl = kabPath.hebrewLetter || {};
const hebrewLetterId = normalizeHebrewLetterId(hl.transliteration); const hebrewLetterId = normalizeHebrewLetterId(hl.transliteration);
const hebrewLetterLabel = hl.transliteration || hl.char || ""; const hebrewLetterLabel = hl.transliteration || hl.char || "";
sections.push(`<div class="planet-meta-card"> sections.push(`<div class="planet-meta-card">
<strong>Kabbalah & Major Arcana</strong> <strong>${tarotAccessEnabled ? "Kabbalah & Major Arcana" : "Kabbalah Path"}</strong>
<div class="planet-text"> <div class="planet-text">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px"> <div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">
<span class="zod-hebrew-glyph">${hl.char || ""}</span> <span class="zod-hebrew-glyph">${hl.char || ""}</span>
@@ -199,7 +205,7 @@
</div> </div>
</div> </div>
<dl class="alpha-dl"> <dl class="alpha-dl">
<dt>Trump Card</dt><dd>${kabPath.tarot?.card ? `<button class="detail-inline-link" data-nav="trump" data-trump-number="${kabPath.tarot?.trumpNumber}">${kabPath.tarot.card}</button>` : ""}</dd> ${tarotAccessEnabled ? `<dt>Trump Card</dt><dd>${kabPath.tarot?.card ? `<button class="detail-inline-link" data-nav="trump" data-trump-number="${kabPath.tarot?.trumpNumber}">${kabPath.tarot.card}</button>` : "—"}</dd>` : ""}
<dt>Intelligence</dt><dd>${kabPath.intelligence || ""}</dd> <dt>Intelligence</dt><dd>${kabPath.intelligence || ""}</dd>
</dl> </dl>
</div> </div>
@@ -211,6 +217,13 @@
const decanRows = decans.map((d) => { const decanRows = decans.map((d) => {
const ord = ["1st","2nd","3rd"][d.index - 1] || d.index; const ord = ["1st","2nd","3rd"][d.index - 1] || d.index;
const sym = PLANET_SYMBOLS[d.rulerPlanetId] || ""; const sym = PLANET_SYMBOLS[d.rulerPlanetId] || "";
if (!tarotAccessEnabled) {
return `<div class="zod-decan-row">
<span class="zod-decan-ord">${ord}</span>
<span class="zod-decan-planet">${sym ? `${sym} ` : ""}<button class="detail-inline-link" data-nav="planet" data-planet-id="${d.rulerPlanetId}">${cap(d.rulerPlanetId)}</button></span>
</div>`;
}
return `<div class="zod-decan-row"> return `<div class="zod-decan-row">
<span class="zod-decan-ord">${ord}</span> <span class="zod-decan-ord">${ord}</span>
<span class="zod-decan-planet">${sym ? `${sym} ` : ""}<button class="detail-inline-link" data-nav="planet" data-planet-id="${d.rulerPlanetId}">${cap(d.rulerPlanetId)}</button></span> <span class="zod-decan-planet">${sym ? `${sym} ` : ""}<button class="detail-inline-link" data-nav="planet" data-planet-id="${d.rulerPlanetId}">${cap(d.rulerPlanetId)}</button></span>
@@ -220,7 +233,7 @@
</div>`; </div>`;
}).join(""); }).join("");
sections.push(`<div class="planet-meta-card"> sections.push(`<div class="planet-meta-card">
<strong>Decans & Minor Arcana</strong> <strong>${tarotAccessEnabled ? "Decans & Minor Arcana" : "Decans"}</strong>
<div class="planet-text">${decanRows}</div> <div class="planet-text">${decanRows}</div>
</div>`); </div>`);
} }
+20 -20
View File
@@ -113,7 +113,7 @@
<button id="close-settings" class="settings-page-back" type="button">Back</button> <button id="close-settings" class="settings-page-back" type="button">Back</button>
</div> </div>
<div class="settings-page-layout"> <div class="settings-page-layout">
<section class="settings-panel"> <section id="settings-tarot-panel" class="settings-panel">
<div class="settings-panel-head"> <div class="settings-panel-head">
<strong>Location And Time</strong> <strong>Location And Time</strong>
<span>Controls calendar rendering, the Now panel, and optional sky background.</span> <span>Controls calendar rendering, the Now panel, and optional sky background.</span>
@@ -1311,45 +1311,45 @@
<script src="node_modules/suncalc/suncalc.js"></script> <script src="node_modules/suncalc/suncalc.js"></script>
<script src="node_modules/astronomy-engine/astronomy.browser.min.js"></script> <script src="node_modules/astronomy-engine/astronomy.browser.min.js"></script>
<script src="app/astro-calcs.js"></script> <script src="app/astro-calcs.js"></script>
<script src="app/app-config.js?v=20260309-gate"></script> <script src="app/app-config.js?v=20260529-access-gating-01"></script>
<script src="app/data-service.js?v=20260527-api-connection-01"></script> <script src="app/data-service.js?v=20260529-access-gating-01"></script>
<script src="app/calendar-events.js"></script> <script src="app/calendar-events.js"></script>
<script src="app/card-images.js?v=20260527-tarot-deck-gallery-01"></script> <script src="app/card-images.js?v=20260527-tarot-deck-gallery-01"></script>
<script src="app/ui-tarot-lightbox.js?v=20260528-tarot-variant-sequence-02"></script> <script src="app/ui-tarot-lightbox.js?v=20260528-tarot-variant-sequence-02"></script>
<script src="app/ui-tarot-house.js?v=20260401-house-top-date-01"></script> <script src="app/ui-tarot-house.js?v=20260401-house-top-date-01"></script>
<script src="app/ui-tarot-relations.js"></script> <script src="app/ui-tarot-relations.js"></script>
<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=20260529-access-gating-01"></script>
<script src="app/ui-natal.js"></script> <script src="app/ui-natal.js"></script>
<script src="app/tarot-database-builders.js?v=20260424-decan-ranges-01"></script> <script src="app/tarot-database-builders.js?v=20260424-decan-ranges-01"></script>
<script src="app/tarot-database-assembly.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/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?v=20260424-association-web-02"></script> <script src="app/ui-calendar-detail-panels.js?v=20260529-access-gating-02"></script>
<script src="app/ui-calendar-detail.js?v=20260424-detail-inline-links-02"></script> <script src="app/ui-calendar-detail.js?v=20260529-access-gating-02"></script>
<script src="app/ui-calendar-data.js?v=20260424-decan-ranges-01"></script> <script src="app/ui-calendar-data.js?v=20260424-decan-ranges-01"></script>
<script src="app/ui-sequence-nav.js?v=20260528-sequence-nav-01"></script> <script src="app/ui-sequence-nav.js?v=20260528-sequence-nav-01"></script>
<script src="app/ui-calendar.js?v=20260528-sequence-nav-01"></script> <script src="app/ui-calendar.js?v=20260528-sequence-nav-01"></script>
<script src="app/ui-holidays-data.js"></script> <script src="app/ui-holidays-data.js"></script>
<script src="app/ui-holidays-render.js?v=20260424-association-web-01"></script> <script src="app/ui-holidays-render.js?v=20260529-access-gating-02"></script>
<script src="app/ui-holidays.js?v=20260528-sequence-nav-01"></script> <script src="app/ui-holidays.js?v=20260528-sequence-nav-01"></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=20260527-tarot-deck-gallery-02"></script> <script src="app/ui-tarot-detail.js?v=20260527-tarot-deck-gallery-02"></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=20260528-tarot-variant-sequence-02"></script> <script src="app/ui-tarot.js?v=20260528-tarot-variant-sequence-02"></script>
<script src="app/ui-planets-references.js"></script> <script src="app/ui-planets-references.js"></script>
<script src="app/ui-planets.js?v=20260528-sequence-nav-01"></script> <script src="app/ui-planets.js?v=20260529-access-gating-02"></script>
<script src="app/ui-cycles.js?v=20260424-detail-inline-links-02"></script> <script src="app/ui-cycles.js?v=20260424-detail-inline-links-02"></script>
<script src="app/ui-elements.js?v=20260424-association-web-01"></script> <script src="app/ui-elements.js?v=20260529-access-gating-02"></script>
<script src="app/ui-audio-notes.js?v=20260314-audio-notes-02"></script> <script src="app/ui-audio-notes.js?v=20260314-audio-notes-02"></script>
<script src="app/ui-audio-circle.js?v=20260314-audio-circle-01"></script> <script src="app/ui-audio-circle.js?v=20260314-audio-circle-01"></script>
<script src="app/ui-iching-references.js"></script> <script src="app/ui-iching-references.js"></script>
<script src="app/ui-iching.js?v=20260424-association-web-01"></script> <script src="app/ui-iching.js?v=20260529-access-gating-02"></script>
<script src="app/ui-rosicrucian-cross.js"></script> <script src="app/ui-rosicrucian-cross.js"></script>
<script src="app/ui-kabbalah-detail.js?v=20260528-kabbalah-worlds-03"></script> <script src="app/ui-kabbalah-detail.js?v=20260529-access-gating-02"></script>
<script src="app/ui-kabbalah-views.js"></script> <script src="app/ui-kabbalah-views.js"></script>
<script src="app/ui-kabbalah.js?v=20260528-kabbalah-cross-split-06"></script> <script src="app/ui-kabbalah.js?v=20260528-kabbalah-cross-split-06"></script>
<script src="app/ui-cube-detail.js?v=20260424-association-web-02"></script> <script src="app/ui-cube-detail.js?v=20260529-access-gating-02"></script>
<script src="app/ui-cube-chassis.js?v=20260424-cube-fixes-01"></script> <script src="app/ui-cube-chassis.js?v=20260424-cube-fixes-01"></script>
<script src="app/ui-cube-math.js"></script> <script src="app/ui-cube-math.js"></script>
<script src="app/ui-cube-selection.js?v=20260424-cube-fixes-01"></script> <script src="app/ui-cube-selection.js?v=20260424-cube-fixes-01"></script>
@@ -1357,12 +1357,12 @@
<script src="app/ui-alphabet-gematria.js?v=20260323-word-meta-01"></script> <script src="app/ui-alphabet-gematria.js?v=20260323-word-meta-01"></script>
<script src="app/ui-alphabet-browser.js?v=20260309-enochian-api"></script> <script src="app/ui-alphabet-browser.js?v=20260309-enochian-api"></script>
<script src="app/ui-alphabet-references.js"></script> <script src="app/ui-alphabet-references.js"></script>
<script src="app/ui-alphabet-detail.js?v=20260424-association-web-01"></script> <script src="app/ui-alphabet-detail.js?v=20260529-access-gating-02"></script>
<script src="app/ui-alphabet-kabbalah.js"></script> <script src="app/ui-alphabet-kabbalah.js"></script>
<script src="app/ui-alphabet.js?v=20260424-detail-inline-links-02"></script> <script src="app/ui-alphabet.js?v=20260424-detail-inline-links-02"></script>
<script src="app/ui-alphabet-text.js?v=20260315-text-search-ui-01"></script> <script src="app/ui-alphabet-text.js?v=20260315-text-search-ui-01"></script>
<script src="app/ui-zodiac-references.js"></script> <script src="app/ui-zodiac-references.js"></script>
<script src="app/ui-zodiac.js?v=20260424-association-web-02"></script> <script src="app/ui-zodiac.js?v=20260529-access-gating-02"></script>
<script src="app/ui-quiz-bank-builtins-domains.js"></script> <script src="app/ui-quiz-bank-builtins-domains.js"></script>
<script src="app/ui-quiz-bank-builtins.js"></script> <script src="app/ui-quiz-bank-builtins.js"></script>
<script src="app/ui-quiz-bank.js"></script> <script src="app/ui-quiz-bank.js"></script>
@@ -1372,20 +1372,20 @@
<script src="app/quiz-connections.js"></script> <script src="app/quiz-connections.js"></script>
<script src="app/ui-gods-references.js"></script> <script src="app/ui-gods-references.js"></script>
<script src="app/ui-gods.js"></script> <script src="app/ui-gods.js"></script>
<script src="app/ui-enochian.js"></script> <script src="app/ui-enochian.js?v=20260529-access-gating-02"></script>
<script src="app/ui-numbers-detail.js"></script> <script src="app/ui-numbers-detail.js?v=20260529-access-gating-02"></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=20260424-frame-export-01"></script> <script src="app/ui-tarot-frame.js?v=20260424-frame-export-01"></script>
<script src="app/ui-settings.js?v=20260527-tarot-deck-gallery-01"></script> <script src="app/ui-settings.js?v=20260529-access-gating-01"></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=20260528-kabbalah-cross-split-06"></script> <script src="app/ui-navigation.js?v=20260528-kabbalah-cross-split-06"></script>
<script src="app/ui-calendar-formatting.js?v=20260307b"></script> <script src="app/ui-calendar-formatting.js?v=20260307b"></script>
<script src="app/ui-calendar-visuals.js?v=20260307b"></script> <script src="app/ui-calendar-visuals.js?v=20260307b"></script>
<script src="app/ui-home-calendar.js?v=20260415-stellarium-toggle-01"></script> <script src="app/ui-home-calendar.js?v=20260415-stellarium-toggle-01"></script>
<script src="app/ui-section-state.js?v=20260528-kabbalah-cross-split-06"></script> <script src="app/ui-section-state.js?v=20260529-access-gating-01"></script>
<script src="app/app-runtime.js?v=20260309-gate"></script> <script src="app/app-runtime.js?v=20260529-access-gating-01"></script>
<script src="app.js?v=20260528-kabbalah-cross-split-06"></script> <script src="app.js?v=20260529-access-gating-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>