From 254f488ecabbee9c851cbb7c708d91ad07119d15 Mon Sep 17 00:00:00 2001 From: Nose Date: Fri, 29 May 2026 00:27:03 -0700 Subject: [PATCH] update commit message --- app.js | 46 ++++++++ app/app-config.js | 90 ++++++++++++++++ app/app-runtime.js | 14 ++- app/data-service.js | 30 +++++- app/ui-alphabet-detail.js | 20 ++-- app/ui-calendar-detail-panels.js | 12 +++ app/ui-calendar-detail.js | 6 +- app/ui-cube-detail.js | 16 +-- app/ui-elements.js | 173 ++++++++++++++++--------------- app/ui-enochian.js | 11 +- app/ui-holidays-render.js | 6 +- app/ui-iching.js | 16 ++- app/ui-kabbalah-detail.js | 60 +++++++---- app/ui-navigation.js | 17 ++- app/ui-now.js | 42 ++++++-- app/ui-numbers-detail.js | 80 ++++++++------ app/ui-planets.js | 8 +- app/ui-section-state.js | 6 +- app/ui-settings.js | 63 +++++++++-- app/ui-zodiac.js | 19 +++- index.html | 40 +++---- 21 files changed, 565 insertions(+), 210 deletions(-) diff --git a/app.js b/app.js index dc199f2..747daf5 100644 --- a/app.js +++ b/app.js @@ -75,6 +75,7 @@ const openAudioNotesEl = document.getElementById("open-audio-notes"); const openTarotEl = document.getElementById("open-tarot"); const openTarotFrameEl = document.getElementById("open-tarot-frame"); const openTarotHouseEl = document.getElementById("open-tarot-house"); +const settingsTarotPanelEl = document.getElementById("settings-tarot-panel"); const openAstronomyEl = document.getElementById("open-astronomy"); const openPlanetsEl = document.getElementById("open-planets"); 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 connectionGateStatusEl = document.getElementById("connection-gate-status"); 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 = { nowHourEl: document.getElementById("now-hour"), @@ -128,6 +132,34 @@ const nowElements = { 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 = { hourStart: 0, hourEnd: 24, @@ -245,6 +277,7 @@ appRuntime.init?.({ nowElements, calendarVisualsUi, homeUi, + hasTarotAccess: () => hasTarotFeatureAccess(), onStatus: (text) => setStatus(text), services: { getCenteredWeekStartDay, @@ -390,6 +423,10 @@ function getConnectionSettingsFromGate() { } function warmAllDeckImagesInBackground() { + if (!hasTarotFeatureAccess()) { + return; + } + const activeDeckId = String(window.TarotCardImages?.getActiveDeck?.() || "").trim(); window.TarotCardImages?.scheduleAllDeckImagePreload?.({ @@ -426,6 +463,9 @@ async function ensureConnectedApp(nextConnectionSettings = null) { syncConnectionGateInputs(configuredConnection); } + window.TarotAppConfig?.updateConnectionAccess?.(probeResult); + syncTarotFeatureVisibility(); + hideConnectionGate(); if (!hasRenderedConnectedShell) { sectionStateUi.setActiveSection?.("home"); @@ -500,6 +540,7 @@ sectionStateUi.init?.({ settingsUi, calendarVisualsUi, homeUi, + isSectionAccessible: (section) => isSectionAccessible(section), getReferenceData: () => appRuntime.getReferenceData?.() || null, getMagickDataset: () => appRuntime.getMagickDataset?.() || null, elements: { @@ -637,6 +678,10 @@ if (nowOverlayToggleEl && nowPanelEl) { syncNowOverlayVisibility(); } +document.addEventListener("connection:access-updated", () => { + syncTarotFeatureVisibility(); +}); + navigationUi.init?.({ tarotSpreadUi, getActiveSection: () => sectionStateUi.getActiveSection?.() || "home", @@ -714,5 +759,6 @@ window.TarotNatal = { const initialSettings = settingsUi.loadInitialSettingsAndApply?.() || { ...DEFAULT_SETTINGS }; homeUi.syncNowSkyBackground?.({ latitude: initialSettings.latitude, longitude: initialSettings.longitude }, true); +syncTarotFeatureVisibility(); bindConnectionGate(); void ensureConnectedApp(); diff --git a/app/app-config.js b/app/app-config.js index 360cd0b..1ac4eea 100644 --- a/app/app-config.js +++ b/app/app-config.js @@ -1,6 +1,20 @@ (function () { const apiBaseUrlStorageKey = "tarot-time-api-base-url"; 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) { return String(value || "") @@ -12,6 +26,55 @@ 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) { return { apiBaseUrl: normalizeBaseUrl(settings?.apiBaseUrl), @@ -90,11 +153,13 @@ stripLegacyCredentialParams(); const initialConnectionSettings = readConfiguredConnectionSettings(); + const initialConnectionAccess = normalizeConnectionAccess(null); window.TarotAppConfig = { ...(window.TarotAppConfig || {}), apiBaseUrl: initialConnectionSettings.apiBaseUrl, apiKey: initialConnectionSettings.apiKey, + connectionAccess: initialConnectionAccess, getApiBaseUrl() { return normalizeBaseUrl(this.apiBaseUrl); }, @@ -110,6 +175,31 @@ 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 = {}) { const previous = this.getConnectionSettings(); const current = normalizeConnectionSettings({ diff --git a/app/app-runtime.js b/app/app-runtime.js index c3d2553..048af24 100644 --- a/app/app-runtime.js +++ b/app/app-runtime.js @@ -11,6 +11,7 @@ calendarVisualsUi: null, homeUi: null, onStatus: null, + hasTarotAccess: () => false, services: {}, ensure: {} }; @@ -112,12 +113,15 @@ renderInProgress = true; try { + const tarotAccessEnabled = config.hasTarotAccess?.() === true; currentGeo = parseGeoInput(); config.homeUi?.syncNowPanelTheme?.(new Date()); config.homeUi?.syncNowSkyBackground?.(currentGeo); 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([ referenceData ? Promise.resolve(referenceData) : config.services.loadReferenceData?.(), magickDataset @@ -129,7 +133,9 @@ magickDataset = loadedMagick; } - config.ensure.ensureTarotSection?.(referenceData, magickDataset); + if (tarotAccessEnabled) { + config.ensure.ensureTarotSection?.(referenceData, magickDataset); + } config.ensure.ensurePlanetSection?.(referenceData, magickDataset); config.ensure.ensureCyclesSection?.(referenceData); config.ensure.ensureIChingSection?.(referenceData); @@ -152,7 +158,9 @@ 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(); } catch (error) { setStatus(error?.message || "Failed to render calendar."); diff --git a/app/data-service.js b/app/data-service.js index f2b58cc..c8533d6 100644 --- a/app/data-service.js +++ b/app/data-service.js @@ -168,6 +168,12 @@ .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() { return Boolean(getApiBaseUrl()); } @@ -614,7 +620,7 @@ } 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) { return { @@ -630,18 +636,32 @@ return { ok: false, 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 { ok: true, reason: "connected", - message: "Connected.", + message: tarotAvailable ? "Connected." : "Connected. Tarot features are unavailable for this API key.", health, auth: health?.auth || null, - deckCount: Array.isArray(decksPayload?.decks) ? decksPayload.decks.length : null + deckCount, + capabilities: { + tarot: tarotAvailable, + adminApiManagement: hasAdminCapability(health?.auth) + } }; } catch (_error) { return { diff --git a/app/ui-alphabet-detail.js b/app/ui-alphabet-detail.js index 960fdca..9d7a346 100644 --- a/app/ui-alphabet-detail.js +++ b/app/ui-alphabet-detail.js @@ -29,6 +29,10 @@ return `${String(normalized).split("").join(" + ")} = ${digitalRoot}`; } + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + function renderPositionDigitalRootCard(letter, alphabet, context, orderLabel) { const index = Number(letter?.index); if (!Number.isFinite(index)) { @@ -145,6 +149,10 @@ } function renderTarotValue(value, context) { + if (!hasTarotAccess()) { + return ""; + } + const label = String(value || "").trim(); if (!label) { return "—"; @@ -408,11 +416,9 @@ } if (letter.kabbalahPathNumber) { - const tarotPart = letter.tarot - ? `
Tarot Card
${letter.tarot.card} (Trump ${letter.tarot.trumpNumber})
` - : ""; + const tarotAccessEnabled = hasTarotAccess(); 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 }) : ""; const cubePlacement = context.getCubePlacementForHebrewLetter(letter.hebrewLetterId, letter.kabbalahPathNumber); @@ -420,10 +426,10 @@ "hebrew-letter-id": letter.hebrewLetterId, "path-no": letter.kabbalahPathNumber }); - sections.push(context.card("Kabbalah & Tarot", ` + sections.push(context.card(tarotAccessEnabled ? "Kabbalah & Tarot" : "Kabbalah", `
Path Number
${kabBtn}
- ${letter.tarot ? `
Tarot Card
${tarotBtn} (Trump ${letter.tarot.trumpNumber})
` : ""} + ${tarotAccessEnabled && letter.tarot ? `
Tarot Card
${tarotBtn} (Trump ${letter.tarot.trumpNumber})
` : ""}
${cubeBtn ? `
Cube placement ${cubeBtn}
` : ""} `)); @@ -580,7 +586,7 @@
English Letters
${englishRefs.join(" / ") || "—"}
Transliteration
${letter.transliteration || "—"}
Element / Planet
${renderElementOrPlanetValue(letter.elementOrPlanet, context)}
-
Tarot
${renderTarotValue(letter.tarot, context)}
+ ${hasTarotAccess() ? `
Tarot
${renderTarotValue(letter.tarot, context)}
` : ""}
Numerology
${letter.numerology || "—"}
Glyph Source
API asset: img/enochian (sourced from dCode set)
Position
#${letter.index} of 21
diff --git a/app/ui-calendar-detail-panels.js b/app/ui-calendar-detail-panels.js index c4860e3..76231dc 100644 --- a/app/ui-calendar-detail-panels.js +++ b/app/ui-calendar-detail-panels.js @@ -1,6 +1,10 @@ (function () { "use strict"; + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + function findSignIdByAstrologyName(name, context) { const { api, getState } = context; const token = api.normalizeCalendarText(name); @@ -140,6 +144,10 @@ } function renderMajorArcanaCard(context) { + if (!hasTarotAccess()) { + return ""; + } + const { month, api } = context; const selectedDay = api.getSelectedDayFilterContext(month); const allRows = buildMajorArcanaRowsForMonth(context); @@ -191,6 +199,10 @@ } function renderDecanTarotCard(context) { + if (!hasTarotAccess()) { + return ""; + } + const { month, api } = context; const selectedDay = api.getSelectedDayFilterContext(month); const allRows = api.buildDecanTarotRowsForMonth(month); diff --git a/app/ui-calendar-detail.js b/app/ui-calendar-detail.js index a1f2745..d2f8b27 100644 --- a/app/ui-calendar-detail.js +++ b/app/ui-calendar-detail.js @@ -52,6 +52,10 @@ Object.assign(api, config || {}); } + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + function getState() { return api.getState?.() || {}; } @@ -158,7 +162,7 @@ } } - if (associations.tarotCard) { + if (associations.tarotCard && hasTarotAccess()) { const explicitTrumpNumber = Number(associations.tarotTrumpNumber); const tarotTrumpNumber = Number.isFinite(explicitTrumpNumber) ? explicitTrumpNumber : null; const tarotLabel = api.getDisplayTarotName(associations.tarotCard, tarotTrumpNumber); diff --git a/app/ui-cube-detail.js b/app/ui-cube-detail.js index 27a7683..94d04fb 100644 --- a/app/ui-cube-detail.js +++ b/app/ui-cube-detail.js @@ -104,6 +104,10 @@ return list; } + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + function createAstrologyValue(astrology, context) { const type = toDisplayText(astrology?.type).toLowerCase(); const name = toDisplayText(astrology?.name); @@ -150,7 +154,7 @@ const tarotCard = toDisplayText(pathEntry?.tarot?.card); const tarotTrumpNumber = context.toFiniteNumber(pathEntry?.tarot?.trumpNumber); - if (tarotCard || tarotTrumpNumber != null) { + if (hasTarotAccess() && (tarotCard || tarotTrumpNumber != null)) { rows.push({ label: "Tarot", value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", { @@ -223,7 +227,7 @@ { label: "Element", value: createElementValue(center?.element, context) || center?.element } ]; - if (centerTarotCard || centerTrumpNo != null) { + if (hasTarotAccess() && (centerTarotCard || centerTrumpNo != null)) { summaryRows.push({ label: "Tarot", value: createInlineEventLink(centerTarotCard || `Trump ${centerTrumpNo}`, "nav:tarot-trump", { @@ -327,7 +331,7 @@ } ]; - if (tarotCard || tarotTrumpNumber != null) { + if (hasTarotAccess() && (tarotCard || tarotTrumpNumber != null)) { connectorRows.push({ label: "Tarot", value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", { @@ -428,7 +432,7 @@ } ]; - if (tarotCard || tarotTrumpNumber != null) { + if (hasTarotAccess() && (tarotCard || tarotTrumpNumber != null)) { edgeRows.push({ label: "Tarot", value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", { @@ -562,7 +566,7 @@ } else { const directTarotCard = toDisplayText(wallAssociations?.tarotCard || wall?.tarotCard); const directTrumpNumber = toFiniteNumber(wallAssociations?.tarotTrumpNumber); - if (directTarotCard || directTrumpNumber != null) { + if (hasTarotAccess() && (directTarotCard || directTrumpNumber != null)) { wallRows.push({ label: "Tarot", value: createInlineEventLink(directTarotCard || `Trump ${directTrumpNumber}`, "nav:tarot-trump", { @@ -734,7 +738,7 @@ } ]; - if (tarotCard || tarotTrumpNumber != null) { + if (hasTarotAccess() && (tarotCard || tarotTrumpNumber != null)) { edgeRows.push({ label: "Tarot", value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", { diff --git a/app/ui-elements.js b/app/ui-elements.js index 5f377ff..6fdfc41 100644 --- a/app/ui-elements.js +++ b/app/ui-elements.js @@ -42,6 +42,10 @@ earth: "Disks" }; + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + const SMALL_CARD_GROUPS = [ { label: "2–4", modality: "Cardinal", numbers: [2, 3, 4] }, { label: "5–7", modality: "Fixed", numbers: [5, 6, 7] }, @@ -324,106 +328,111 @@ detailsCard.append(detailsTitle, detailsList); - const tarotCard = document.createElement("div"); - tarotCard.className = "planet-meta-card"; + const detailCards = [detailsCard]; - const tarotTitle = document.createElement("strong"); - tarotTitle.textContent = "Tarot Correspondence"; + if (hasTarotAccess()) { + const tarotCard = document.createElement("div"); + tarotCard.className = "planet-meta-card"; - const tarotParts = []; - if (entry.aceCardName) { - 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 tarotTitle = document.createElement("strong"); + tarotTitle.textContent = "Tarot Correspondence"; - const tarotText = tarotParts.length - ? createInlineParagraph(tarotParts) - : document.createTextNode("--"); - - tarotCard.append(tarotTitle, tarotText); - - if (entry.courtCardNames.length) { - 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 = ` - ${group.rangeLabel} · ${group.modality} - ${group.signName || "--"} - `; - row.appendChild(head); - - if (group.signId) { - row.appendChild(createInlineParagraph([ - "Sign: ", - createInlineButton(group.signName, () => { - document.dispatchEvent(new CustomEvent("nav:zodiac", { - detail: { signId: group.signId } + const tarotParts = []; + if (entry.aceCardName) { + 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)`); } - if ((group.cardNames || []).length) { - const cardParts = ["Cards: "]; - group.cardNames.forEach((cardName, index) => { + const tarotText = tarotParts.length + ? createInlineParagraph(tarotParts) + : document.createTextNode("--"); + + tarotCard.append(tarotTitle, tarotText); + + if (entry.courtCardNames.length) { + const courtParts = ["Court cards: "]; + entry.courtCardNames.forEach((cardName, index) => { if (index > 0) { - cardParts.push(", "); + courtParts.push(", "); } - cardParts.push(createInlineButton(cardName, () => { + courtParts.push(createInlineButton(cardName, () => { document.dispatchEvent(new CustomEvent("nav:tarot-trump", { 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 = ` + ${group.rangeLabel} · ${group.modality} + ${group.signName || "--"} + `; + 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); } diff --git a/app/ui-enochian.js b/app/ui-enochian.js index d02c01a..cddf52c 100644 --- a/app/ui-enochian.js +++ b/app/ui-enochian.js @@ -30,6 +30,10 @@ lettersById: new Map() }; + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + function getElements() { return { listEl: document.getElementById("enochian-list"), @@ -306,10 +310,13 @@ const detailRows = [ ["Pronunciation", letterData.pronounciation], ["Planet / Element", letterData["planet/element"]], - ["Tarot", letterData.tarot], ["Gematria", letterData.gematria] ]; + if (hasTarotAccess()) { + detailRows.splice(2, 0, ["Tarot", letterData.tarot]); + } + detailRows.forEach(([label, value]) => { if (value === undefined || value === null || String(value).trim() === "") { return; @@ -324,7 +331,7 @@ navRow.className = "enoch-letter-row"; const tarotCardName = resolveTarotCardName(letterData.tarot); - if (tarotCardName) { + if (hasTarotAccess() && tarotCardName) { const tarotBtn = document.createElement("button"); tarotBtn.type = "button"; tarotBtn.className = "enoch-nav-btn"; diff --git a/app/ui-holidays-render.js b/app/ui-holidays-render.js index b931e46..8888aa7 100644 --- a/app/ui-holidays-render.js +++ b/app/ui-holidays-render.js @@ -73,6 +73,10 @@ return current; } + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + function buildInlineNavButton(label, nav, attrs = {}) { const dataAttrs = Object.entries(attrs) .map(([key, value]) => `data-${key}="${value}"`) @@ -109,7 +113,7 @@ } } - if (associations.tarotCard) { + if (associations.tarotCard && hasTarotAccess()) { const trumpNumber = resolveTarotTrumpNumber(associations.tarotCard); const explicitTrumpNumber = Number(associations.tarotTrumpNumber); const tarotTrumpNumber = Number.isFinite(explicitTrumpNumber) ? explicitTrumpNumber : trumpNumber; diff --git a/app/ui-iching.js b/app/ui-iching.js index 72f83c6..ad4c43d 100644 --- a/app/ui-iching.js +++ b/app/ui-iching.js @@ -20,6 +20,10 @@ selectedNumber: null }; + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + const ICHING_PLANET_BY_PLANET_ID = { sol: "Sun", luna: "Moon", @@ -235,7 +239,8 @@ elements.ichingDetailPlanetEl.textContent = "--"; } if (elements.ichingDetailTarotEl) { - elements.ichingDetailTarotEl.textContent = "--"; + elements.ichingDetailTarotEl.hidden = !hasTarotAccess(); + elements.ichingDetailTarotEl.textContent = hasTarotAccess() ? "--" : ""; } if (elements.ichingDetailCalendarEl) { clearChildren(elements.ichingDetailCalendarEl); @@ -327,6 +332,15 @@ return; } + if (!hasTarotAccess()) { + clearChildren(elements.ichingDetailTarotEl); + elements.ichingDetailTarotEl.hidden = true; + elements.ichingDetailTarotEl.textContent = ""; + return; + } + + elements.ichingDetailTarotEl.hidden = false; + clearChildren(elements.ichingDetailTarotEl); const upperKey = normalizeSearchValue(entry?.upperTrigram); diff --git a/app/ui-kabbalah-detail.js b/app/ui-kabbalah-detail.js index 5a5b2d9..add5de5 100644 --- a/app/ui-kabbalah-detail.js +++ b/app/ui-kabbalah-detail.js @@ -26,6 +26,10 @@ const MINOR_SUITS = ["Wands", "Cups", "Swords", "Disks"]; + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + const DEFAULT_FOUR_QABALISTIC_WORLD_LAYERS = [ { slot: "Yod", @@ -167,6 +171,10 @@ } function buildTarotAttributionCard(attribution) { + if (!hasTarotAccess()) { + return null; + } + const minorCards = buildMinorTarotNames(attribution); if (minorCards.length) { const parts = []; @@ -485,7 +493,10 @@ elements.detailBodyEl.innerHTML = ""; elements.detailBodyEl.appendChild(buildPlanetLuminaryCard(seph.planet, context)); 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) { elements.detailBodyEl.appendChild( @@ -500,7 +511,7 @@ const card = document.createElement("div"); card.className = "planet-meta-card kab-wide-card"; const chips = connected.map((entry) => - `` + `` + `${entry.hebrewLetter?.char || ""} ${entry.pathNumber}` + `` ).join(""); @@ -533,39 +544,42 @@ 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 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 : ""}` : "—"; elements.detailNameEl.textContent = `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.appendChild(buildConnectsCard(path, fromName, toName)); elements.detailBodyEl.appendChild(buildHebrewLetterCard(letter, context)); elements.detailBodyEl.appendChild(buildAstrologyCard(path.astrology, context)); - const tarotMetaCard = document.createElement("div"); - tarotMetaCard.className = "planet-meta-card"; - const tarotLabel = document.createElement("strong"); - tarotLabel.textContent = "Tarot"; - tarotMetaCard.appendChild(tarotLabel); - if (path.tarot?.card && path.tarot.trumpNumber != null) { - const tarotBtn = createInlineEventLink( - `${path.tarot.card} · Trump ${path.tarot.trumpNumber}`, - "kab:view-trump", - { trumpNumber: path.tarot.trumpNumber } - ); - tarotBtn.title = "Open in Tarot section"; - tarotMetaCard.appendChild(tarotBtn); - } else { - const tarotP = document.createElement("p"); - tarotP.className = "planet-text"; - tarotP.textContent = tarotStr || "—"; - tarotMetaCard.appendChild(tarotP); + if (tarotAccessEnabled) { + const tarotMetaCard = document.createElement("div"); + tarotMetaCard.className = "planet-meta-card"; + const tarotLabel = document.createElement("strong"); + tarotLabel.textContent = "Tarot"; + tarotMetaCard.appendChild(tarotLabel); + if (path.tarot?.card && path.tarot.trumpNumber != null) { + const tarotBtn = createInlineEventLink( + `${path.tarot.card} · Trump ${path.tarot.trumpNumber}`, + "kab:view-trump", + { trumpNumber: path.tarot.trumpNumber } + ); + tarotBtn.title = "Open in Tarot section"; + tarotMetaCard.appendChild(tarotBtn); + } else { + const tarotP = document.createElement("p"); + tarotP.className = "planet-text"; + 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("Pillar", path.pillar)); diff --git a/app/ui-navigation.js b/app/ui-navigation.js index 1a3ca2d..f055b8a 100644 --- a/app/ui-navigation.js +++ b/app/ui-navigation.js @@ -93,6 +93,10 @@ } async function prepareTarotBrowseDetailView() { + if (window.TarotAppConfig?.hasTarotAccess?.() !== true) { + return false; + } + const ensure = config.ensure || {}; const referenceData = getReferenceData(); const magickDataset = getMagickDataset(); @@ -113,6 +117,7 @@ }); showSectionDetailOnly("tarot"); + return true; } function bindClick(element, handler) { @@ -491,7 +496,11 @@ }); document.addEventListener("nav:tarot-trump", async (event) => { - await prepareTarotBrowseDetailView(); + const tarotReady = await prepareTarotBrowseDetailView(); + if (!tarotReady) { + return; + } + const { trumpNumber, cardName } = event?.detail || {}; if (trumpNumber != null) { @@ -502,7 +511,11 @@ }); document.addEventListener("kab:view-trump", async (event) => { - await prepareTarotBrowseDetailView(); + const tarotReady = await prepareTarotBrowseDetailView(); + if (!tarotReady) { + return; + } + const trumpNumber = event?.detail?.trumpNumber; if (trumpNumber != null) { diff --git a/app/ui-now.js b/app/ui-now.js index 6fa3926..6ca756c 100644 --- a/app/ui-now.js +++ b/app/ui-now.js @@ -58,14 +58,29 @@ } } + function setNowTarotVisibility(imageEl, labelEl, visible) { + if (imageEl) { + imageEl.hidden = !visible; + } + + if (labelEl) { + labelEl.hidden = !visible; + } + } + function applyNowSnapshot(elements, snapshot, timeFormat) { const timestamp = snapshot?.timestamp ? new Date(snapshot.timestamp) : new Date(); const dayKey = String(snapshot?.dayKey || getDateKey(timestamp)); 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) { elements.nowHourEl.textContent = `${currentHour.planet.symbol} ${currentHour.planet.name}`; - if (elements.nowHourTarotEl) { + if (tarotAccessEnabled && elements.nowHourTarotEl) { const hourCardName = currentHour.planet?.tarot?.majorArcana || ""; const hourTrumpNumber = currentHour.planet?.tarot?.number; elements.nowHourTarotEl.textContent = hourCardName @@ -84,7 +99,7 @@ nowUiHelpers.setNowCardImage( elements.nowHourCardEl, - currentHour.planet?.tarot?.majorArcana, + tarotAccessEnabled ? currentHour.planet?.tarot?.majorArcana : null, "Current planetary hour card", currentHour.planet?.tarot?.number ); @@ -92,7 +107,7 @@ elements.nowHourEl.textContent = "--"; elements.nowCountdownEl.textContent = "--"; if (elements.nowHourTarotEl) { - elements.nowHourTarotEl.textContent = "--"; + elements.nowHourTarotEl.textContent = tarotAccessEnabled ? "--" : ""; } if (elements.nowHourNextEl) { elements.nowHourNextEl.textContent = "> --"; @@ -106,12 +121,12 @@ elements.nowMoonEl.textContent = moon ? `${moon.phase} (${Math.round(illuminationFraction * 100)}%)` : "--"; - elements.nowMoonTarotEl.textContent = moon - ? nowUiHelpers.getDisplayTarotName(moonTarot, moon?.tarot?.number) - : "--"; + elements.nowMoonTarotEl.textContent = tarotAccessEnabled + ? (moon ? nowUiHelpers.getDisplayTarotName(moonTarot, moon?.tarot?.number) : "--") + : ""; nowUiHelpers.setNowCardImage( elements.nowMoonCardEl, - moon?.tarot?.majorArcana, + tarotAccessEnabled ? moon?.tarot?.majorArcana : null, "Current moon phase card", moon?.tarot?.number ); @@ -140,12 +155,14 @@ ? Number(decanInfo.signDegree).toFixed(1) : "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); nowUiHelpers.setNowCardImage(elements.nowDecanCardEl, decanInfo.decan.tarotMinorArcana, "Current decan card"); - } else { + } else if (tarotAccessEnabled) { const signTarotName = decanInfo.sign?.tarot?.majorArcana || "--"; elements.nowDecanTarotEl.textContent = signTarotName === "--" ? "--" @@ -156,6 +173,9 @@ "Current decan card", decanInfo.sign?.tarot?.number ); + } else { + elements.nowDecanTarotEl.textContent = ""; + nowUiHelpers.setNowCardImage(elements.nowDecanCardEl, null, "Current decan card"); } if (elements.nowDecanCountdownEl) { @@ -173,7 +193,7 @@ } } else { elements.nowDecanEl.textContent = "--"; - elements.nowDecanTarotEl.textContent = "--"; + elements.nowDecanTarotEl.textContent = tarotAccessEnabled ? "--" : ""; nowUiHelpers.setNowCardImage(elements.nowDecanCardEl, null, "Current decan card"); if (elements.nowDecanCountdownEl) { elements.nowDecanCountdownEl.textContent = "--"; diff --git a/app/ui-numbers-detail.js b/app/ui-numbers-detail.js index be2e413..eb4bfc8 100644 --- a/app/ui-numbers-detail.js +++ b/app/ui-numbers-detail.js @@ -1,6 +1,10 @@ (function () { "use strict"; + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + function getCalendarMonthLinksForNumber(context, value) { const { getReferenceData, normalizeNumberValue, computeDigitalRoot } = context; const referenceData = getReferenceData(); @@ -406,6 +410,10 @@ } function getTarotCardsForDigitalRoot(context, targetRoot, numberEntry = null) { + if (!hasTarotAccess()) { + return []; + } + const { getReferenceData, getMagickDataset, ensureTarotSection, computeDigitalRoot } = context; const referenceData = getReferenceData(); const magickDataset = getMagickDataset(); @@ -558,38 +566,6 @@ 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"); calendarCardEl.className = "numbers-detail-card"; @@ -625,7 +601,45 @@ 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 = { diff --git a/app/ui-planets.js b/app/ui-planets.js index 0ace64c..d60aaaa 100644 --- a/app/ui-planets.js +++ b/app/ui-planets.js @@ -23,6 +23,10 @@ }; let detailNavigator = null; + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + function normalizePlanetToken(value) { return String(value || "") .trim() @@ -290,7 +294,7 @@ if (!correspondence || typeof correspondence !== "object") { const fallback = document.createElement("span"); 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); return; } @@ -309,7 +313,7 @@ containerEl.appendChild(line); } - if (arcana) { + if (arcana && hasTarotAccess()) { const btn = createInlineButton( trumpNo != null ? `${arcanaLabel} · Trump ${trumpNo}` : arcanaLabel, () => { diff --git a/app/ui-section-state.js b/app/ui-section-state.js index 9e5a991..91cad42 100644 --- a/app/ui-section-state.js +++ b/app/ui-section-state.js @@ -38,6 +38,7 @@ let config = { elements: {}, ensure: {}, + isSectionAccessible: () => true, getReferenceData: () => null, getMagickDataset: () => null, calendarVisualsUi: null, @@ -82,7 +83,10 @@ } 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; const elements = config.elements || {}; diff --git a/app/ui-settings.js b/app/ui-settings.js index 5655b0e..5462373 100644 --- a/app/ui-settings.js +++ b/app/ui-settings.js @@ -181,6 +181,10 @@ }; } + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + function syncConnectionInputs() { const { apiBaseUrlEl, apiKeyEl } = getElements(); const connectionSettings = getConnectionSettings(); @@ -251,8 +255,9 @@ const roles = normalizeConnectionValues(auth.roles); const scopes = normalizeConnectionValues(auth.scopes); const authenticated = auth.authenticated === true; + const tarotAccessEnabled = result?.capabilities?.tarot === true; 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"); const permissionsValue = authenticated ? `roles: ${formatConnectionValues(roles)} | scopes: ${formatConnectionValues(scopes)}` @@ -399,6 +404,10 @@ } function getKnownTarotDeckIds() { + if (!hasTarotAccess()) { + return new Set([String(config.defaultSettings?.tarotDeck || "ceremonial-magick").trim().toLowerCase()]); + } + const knownDeckIds = new Set(); const deckOptions = window.TarotCardImages?.getDeckOptions?.(); @@ -419,6 +428,10 @@ } function getFallbackTarotDeckId() { + if (!hasTarotAccess()) { + return String(config.defaultSettings?.tarotDeck || "ceremonial-magick").trim().toLowerCase(); + } + const deckOptions = window.TarotCardImages?.getDeckOptions?.(); if (Array.isArray(deckOptions)) { for (let i = 0; i < deckOptions.length; i += 1) { @@ -433,6 +446,11 @@ } 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 knownDeckIds = getKnownTarotDeckIds(); @@ -571,6 +589,16 @@ 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 previousValue = String(tarotDeckEl.value || "").trim().toLowerCase(); tarotDeckEl.innerHTML = ""; @@ -602,6 +630,24 @@ 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) { const { latEl, lngEl, timeFormatEl, birthDateEl, tarotDeckEl, stellariumBackgroundEl } = getElements(); syncTarotDeckInputOptions(); @@ -619,10 +665,7 @@ stellariumBackgroundEl.checked = normalized.stellariumBackgroundEnabled; } syncStellariumBackgroundAvailability(); - if (window.TarotCardImages?.setActiveDeck) { - window.TarotCardImages.setActiveDeck(normalized.tarotDeck); - } - syncDeckCacheStatus(window.TarotCardImages?.getDeckPreloadStatus?.()); + syncActiveTarotDeck(normalized.tarotDeck); applyExternalSettings(normalized); return normalized; } @@ -842,13 +885,19 @@ document.addEventListener("connection:updated", () => { syncConnectionInputs(); + void refreshConnectionSummary(getConnectionSettings()); + }); + + document.addEventListener("connection:access-updated", () => { syncTarotDeckInputOptions(); - syncDeckCacheStatus(window.TarotCardImages?.getDeckPreloadStatus?.()); + syncActiveTarotDeck(getElements().tarotDeckEl?.value || loadSavedSettings().tarotDeck); void refreshConnectionSummary(getConnectionSettings()); }); document.addEventListener("tarot:deck-cache-status", (event) => { - syncDeckCacheStatus(event?.detail); + if (hasTarotAccess()) { + syncDeckCacheStatus(event?.detail); + } }); } diff --git a/app/ui-zodiac.js b/app/ui-zodiac.js index 1288c67..37f23b9 100644 --- a/app/ui-zodiac.js +++ b/app/ui-zodiac.js @@ -37,6 +37,10 @@ cubePlacementBySignId: new Map() }; + function hasTarotAccess() { + return window.TarotAppConfig?.hasTarotAccess?.() === true; + } + // ── Elements ────────────────────────────────────────────────────────── function getElements() { return { @@ -184,12 +188,14 @@ } // ── Kabbalah Path + Trump ───────────────────────────────────────── + const tarotAccessEnabled = hasTarotAccess(); + if (kabPath) { const hl = kabPath.hebrewLetter || {}; const hebrewLetterId = normalizeHebrewLetterId(hl.transliteration); const hebrewLetterLabel = hl.transliteration || hl.char || ""; sections.push(`
- Kabbalah & Major Arcana + ${tarotAccessEnabled ? "Kabbalah & Major Arcana" : "Kabbalah Path"}
${hl.char || ""} @@ -199,7 +205,7 @@
-
Trump Card
${kabPath.tarot?.card ? `` : "—"}
+ ${tarotAccessEnabled ? `
Trump Card
${kabPath.tarot?.card ? `` : "—"}
` : ""}
Intelligence
${kabPath.intelligence || "—"}
@@ -211,6 +217,13 @@ const decanRows = decans.map((d) => { const ord = ["1st","2nd","3rd"][d.index - 1] || d.index; const sym = PLANET_SYMBOLS[d.rulerPlanetId] || ""; + if (!tarotAccessEnabled) { + return `
+ ${ord} + ${sym ? `${sym} ` : ""} +
`; + } + return `
${ord} ${sym ? `${sym} ` : ""} @@ -220,7 +233,7 @@
`; }).join(""); sections.push(`
- Decans & Minor Arcana + ${tarotAccessEnabled ? "Decans & Minor Arcana" : "Decans"}
${decanRows}
`); } diff --git a/index.html b/index.html index bd61431..da672c8 100644 --- a/index.html +++ b/index.html @@ -113,7 +113,7 @@
-
+
Location And Time Controls calendar rendering, the Now panel, and optional sky background. @@ -1311,45 +1311,45 @@ - - + + - + - - + + - + - + - + - + - + - + @@ -1357,12 +1357,12 @@ - + - + @@ -1372,20 +1372,20 @@ - - + + - + - - - + + +