building new tarot frame component for custom layout
This commit is contained in:
12
app.js
12
app.js
@@ -40,6 +40,7 @@ const holidaySectionEl = document.getElementById("holiday-section");
|
||||
const audioCircleSectionEl = document.getElementById("audio-circle-section");
|
||||
const audioNotesSectionEl = document.getElementById("audio-notes-section");
|
||||
const tarotSectionEl = document.getElementById("tarot-section");
|
||||
const tarotFrameSectionEl = document.getElementById("tarot-frame-section");
|
||||
const tarotHouseSectionEl = document.getElementById("tarot-house-section");
|
||||
const astronomySectionEl = document.getElementById("astronomy-section");
|
||||
const natalSectionEl = document.getElementById("natal-section");
|
||||
@@ -67,6 +68,7 @@ const openAudioEl = document.getElementById("open-audio");
|
||||
const openAudioCircleEl = document.getElementById("open-audio-circle");
|
||||
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 openAstronomyEl = document.getElementById("open-astronomy");
|
||||
const openPlanetsEl = document.getElementById("open-planets");
|
||||
@@ -393,6 +395,11 @@ window.TarotSpreadUi?.init?.({
|
||||
setActiveSection: (section) => sectionStateUi.setActiveSection?.(section)
|
||||
});
|
||||
|
||||
window.TarotFrameUi?.init?.({
|
||||
ensureTarotSection,
|
||||
getCards: () => window.TarotSectionUi?.getCards?.() || []
|
||||
});
|
||||
|
||||
sectionStateUi.init?.({
|
||||
calendar,
|
||||
tarotSpreadUi,
|
||||
@@ -411,6 +418,7 @@ sectionStateUi.init?.({
|
||||
audioCircleSectionEl,
|
||||
audioNotesSectionEl,
|
||||
tarotSectionEl,
|
||||
tarotFrameSectionEl,
|
||||
tarotHouseSectionEl,
|
||||
astronomySectionEl,
|
||||
natalSectionEl,
|
||||
@@ -438,6 +446,7 @@ sectionStateUi.init?.({
|
||||
openAudioCircleEl,
|
||||
openAudioNotesEl,
|
||||
openTarotEl,
|
||||
openTarotFrameEl,
|
||||
openTarotHouseEl,
|
||||
openAstronomyEl,
|
||||
openPlanetsEl,
|
||||
@@ -460,6 +469,7 @@ sectionStateUi.init?.({
|
||||
},
|
||||
ensure: {
|
||||
ensureTarotSection,
|
||||
ensureTarotFrameSection: window.TarotFrameUi?.ensureTarotFrameSection,
|
||||
ensurePlanetSection,
|
||||
ensureCyclesSection,
|
||||
ensureElementsSection,
|
||||
@@ -540,6 +550,7 @@ navigationUi.init?.({
|
||||
openAudioCircleEl,
|
||||
openAudioNotesEl,
|
||||
openTarotEl,
|
||||
openTarotFrameEl,
|
||||
openTarotHouseEl,
|
||||
openAstronomyEl,
|
||||
openPlanetsEl,
|
||||
@@ -562,6 +573,7 @@ navigationUi.init?.({
|
||||
},
|
||||
ensure: {
|
||||
ensureTarotSection,
|
||||
ensureTarotFrameSection: window.TarotFrameUi?.ensureTarotFrameSection,
|
||||
ensurePlanetSection,
|
||||
ensureCyclesSection,
|
||||
ensureElementsSection,
|
||||
|
||||
247
app/navigation-detail-test-harness.js
Normal file
247
app/navigation-detail-test-harness.js
Normal file
@@ -0,0 +1,247 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const DEFAULT_TIMEOUT = 10000;
|
||||
const DEFAULT_INTERVAL = 50;
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => {
|
||||
window.setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
async function waitFor(predicate, options = {}) {
|
||||
const timeout = Number.isFinite(Number(options.timeout)) ? Number(options.timeout) : DEFAULT_TIMEOUT;
|
||||
const interval = Number.isFinite(Number(options.interval)) ? Number(options.interval) : DEFAULT_INTERVAL;
|
||||
const deadline = Date.now() + timeout;
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
try {
|
||||
if (await predicate()) {
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
|
||||
await sleep(interval);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function textById(id) {
|
||||
const element = document.getElementById(id);
|
||||
return element instanceof HTMLElement ? String(element.textContent || "").trim() : "";
|
||||
}
|
||||
|
||||
function getSectionElement(selector) {
|
||||
const element = document.querySelector(selector);
|
||||
return element instanceof HTMLElement ? element : null;
|
||||
}
|
||||
|
||||
function getLayoutState(selector) {
|
||||
const layout = getSectionElement(selector);
|
||||
const classes = layout ? Array.from(layout.classList) : [];
|
||||
return {
|
||||
classes,
|
||||
detailOnly: classes.includes("layout-sidebar-collapsed") && !classes.includes("layout-detail-collapsed")
|
||||
};
|
||||
}
|
||||
|
||||
function getSelectedText(sectionSelector) {
|
||||
const section = getSectionElement(sectionSelector);
|
||||
if (!section) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const selected = section.querySelector('[aria-selected="true"]');
|
||||
return selected instanceof HTMLElement ? String(selected.textContent || "").trim() : "";
|
||||
}
|
||||
|
||||
function dispatchNavigationEvent(eventName, detail) {
|
||||
document.dispatchEvent(new CustomEvent(eventName, {
|
||||
detail,
|
||||
bubbles: true
|
||||
}));
|
||||
}
|
||||
|
||||
function createSnapshot(test) {
|
||||
const section = getSectionElement(test.sectionSelector);
|
||||
const layoutState = getLayoutState(test.layoutSelector);
|
||||
|
||||
return {
|
||||
sectionHidden: section ? section.hidden : true,
|
||||
layoutClasses: layoutState.classes,
|
||||
detailOnly: layoutState.detailOnly,
|
||||
detailName: textById(test.detailNameId),
|
||||
detailSub: test.detailSubId ? textById(test.detailSubId) : "",
|
||||
detailBody: test.detailBodyId ? textById(test.detailBodyId) : "",
|
||||
selectedText: getSelectedText(test.sectionSelector)
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForAppReady() {
|
||||
return waitFor(() => {
|
||||
if (document.readyState !== "complete") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const referenceData = window.TarotAppRuntime?.getReferenceData?.();
|
||||
const magickDataset = window.TarotAppRuntime?.getMagickDataset?.();
|
||||
|
||||
return Boolean(
|
||||
typeof window.TarotChromeUi?.showDetailOnly === "function"
|
||||
&& referenceData
|
||||
&& magickDataset
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const TESTS = {
|
||||
tarotTrumpNav: {
|
||||
eventName: "nav:tarot-trump",
|
||||
detail: { trumpNumber: 0 },
|
||||
sectionSelector: "#tarot-section",
|
||||
layoutSelector: "#tarot-browse-view .tarot-layout",
|
||||
detailNameId: "tarot-detail-name",
|
||||
matches: (state) => state.detailName === "The Fool"
|
||||
},
|
||||
kabViewTrump: {
|
||||
eventName: "kab:view-trump",
|
||||
detail: { trumpNumber: 0 },
|
||||
sectionSelector: "#tarot-section",
|
||||
layoutSelector: "#tarot-browse-view .tarot-layout",
|
||||
detailNameId: "tarot-detail-name",
|
||||
matches: (state) => state.detailName === "The Fool"
|
||||
},
|
||||
ichingHexagramNav: {
|
||||
eventName: "nav:iching",
|
||||
detail: { hexagramNumber: 1 },
|
||||
sectionSelector: "#iching-section",
|
||||
layoutSelector: "#iching-section .planet-layout",
|
||||
detailNameId: "iching-detail-name",
|
||||
matches: (state) => /Creative Force/i.test(state.detailName) && /#1/i.test(state.selectedText)
|
||||
},
|
||||
zodiacSignNav: {
|
||||
eventName: "nav:zodiac",
|
||||
detail: { signId: "aries" },
|
||||
sectionSelector: "#zodiac-section",
|
||||
layoutSelector: "#zodiac-section .planet-layout",
|
||||
detailNameId: "zodiac-detail-name",
|
||||
detailSubId: "zodiac-detail-sub",
|
||||
matches: (state) => /Aries/i.test(state.detailSub) && /aries/i.test(state.selectedText)
|
||||
},
|
||||
planetNav: {
|
||||
eventName: "nav:planet",
|
||||
detail: { planetId: "mars" },
|
||||
sectionSelector: "#planet-section",
|
||||
layoutSelector: "#planet-section .planet-layout",
|
||||
detailNameId: "planet-detail-name",
|
||||
matches: (state) => state.detailName !== "--" && /mars/i.test(state.selectedText)
|
||||
},
|
||||
numberNav: {
|
||||
eventName: "nav:number",
|
||||
detail: { value: 1 },
|
||||
sectionSelector: "#numbers-section",
|
||||
layoutSelector: "#numbers-section .numbers-main-layout",
|
||||
detailNameId: "numbers-detail-name",
|
||||
matches: (state) => /Number 1|One/i.test(state.detailName) && /One/i.test(state.selectedText)
|
||||
},
|
||||
elementNav: {
|
||||
eventName: "nav:elements",
|
||||
detail: { elementId: "fire" },
|
||||
sectionSelector: "#elements-section",
|
||||
layoutSelector: "#elements-section .planet-layout",
|
||||
detailNameId: "elements-detail-name",
|
||||
matches: (state) => state.detailName !== "--" && /fire/i.test(state.selectedText)
|
||||
},
|
||||
calendarMonthNav: {
|
||||
eventName: "nav:calendar-month",
|
||||
detail: { calendarId: "gregorian", monthId: "january" },
|
||||
sectionSelector: "#calendar-section",
|
||||
layoutSelector: "#calendar-section .planet-layout",
|
||||
detailNameId: "calendar-detail-name",
|
||||
matches: (state) => /january/i.test(state.detailName) && /january/i.test(state.selectedText)
|
||||
},
|
||||
tarotViewKabPath: {
|
||||
eventName: "tarot:view-kab-path",
|
||||
detail: { pathNumber: 11 },
|
||||
sectionSelector: "#kabbalah-tree-section",
|
||||
layoutSelector: "#kabbalah-tree-section .kab-layout",
|
||||
detailNameId: "kab-detail-name",
|
||||
detailSubId: "kab-detail-sub",
|
||||
detailBodyId: "kab-detail-body",
|
||||
matches: (state) => /Path 11/i.test(state.detailName) && /The Fool/i.test(state.detailSub)
|
||||
}
|
||||
};
|
||||
|
||||
async function runTest(testId, options = {}) {
|
||||
const test = TESTS[testId];
|
||||
if (!test) {
|
||||
throw new Error(`Unknown navigation detail test: ${testId}`);
|
||||
}
|
||||
|
||||
const ready = await waitForAppReady();
|
||||
if (!ready) {
|
||||
return {
|
||||
id: testId,
|
||||
pass: false,
|
||||
error: "App data did not finish loading in time."
|
||||
};
|
||||
}
|
||||
|
||||
dispatchNavigationEvent(test.eventName, test.detail);
|
||||
|
||||
const timeout = Number.isFinite(Number(options.timeout)) ? Number(options.timeout) : DEFAULT_TIMEOUT;
|
||||
const pass = await waitFor(() => {
|
||||
const state = createSnapshot(test);
|
||||
return !state.sectionHidden && state.detailOnly && test.matches(state);
|
||||
}, { timeout });
|
||||
|
||||
const state = createSnapshot(test);
|
||||
return {
|
||||
id: testId,
|
||||
eventName: test.eventName,
|
||||
detail: test.detail,
|
||||
pass,
|
||||
...state,
|
||||
error: pass ? "" : "Destination detail pane did not settle into detail-only mode."
|
||||
};
|
||||
}
|
||||
|
||||
async function runAll(testIds = Object.keys(TESTS), options = {}) {
|
||||
const results = [];
|
||||
for (const testId of testIds) {
|
||||
results.push(await runTest(testId, options));
|
||||
}
|
||||
|
||||
const summary = {
|
||||
total: results.length,
|
||||
passed: results.filter((result) => result.pass).length,
|
||||
failed: results.filter((result) => !result.pass).length
|
||||
};
|
||||
|
||||
const payload = { summary, results };
|
||||
window.__TAROT_NAVIGATION_DETAIL_TEST_RESULTS__ = payload;
|
||||
return payload;
|
||||
}
|
||||
|
||||
window.TarotNavigationDetailTestHarness = {
|
||||
runAll,
|
||||
runTest,
|
||||
listTests: () => Object.keys(TESTS)
|
||||
};
|
||||
|
||||
if (new URLSearchParams(window.location.search).has("navtest")) {
|
||||
waitForAppReady().then((ready) => {
|
||||
if (!ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
runAll().then((result) => {
|
||||
window.__TAROT_NAVIGATION_DETAIL_TEST_LAST_RESULT__ = result;
|
||||
console.info("Navigation detail test harness results", result);
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
466
app/styles.css
466
app/styles.css
@@ -89,6 +89,9 @@
|
||||
.topbar-menu-toggle:hover {
|
||||
background: #3f3f46;
|
||||
}
|
||||
.topbar-settings-toggle {
|
||||
margin-left: 0;
|
||||
}
|
||||
.topbar-actions {
|
||||
display: none;
|
||||
flex: 1 0 100%;
|
||||
@@ -187,6 +190,9 @@
|
||||
.topbar-menu-toggle {
|
||||
min-height: 38px;
|
||||
}
|
||||
.topbar-settings-toggle {
|
||||
min-height: 38px;
|
||||
}
|
||||
.topbar-panel-toggle {
|
||||
min-height: 38px;
|
||||
}
|
||||
@@ -389,12 +395,21 @@
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
#tarot-frame-section {
|
||||
height: calc(100vh - 61px);
|
||||
background: #18181b;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
}
|
||||
#tarot-section[hidden] {
|
||||
display: none;
|
||||
}
|
||||
#tarot-house-section[hidden] {
|
||||
display: none;
|
||||
}
|
||||
#tarot-frame-section[hidden] {
|
||||
display: none;
|
||||
}
|
||||
#planet-section[hidden] {
|
||||
display: none;
|
||||
}
|
||||
@@ -844,12 +859,430 @@
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tarot-frame-view {
|
||||
min-height: 100%;
|
||||
padding: 18px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tarot-frame-shell {
|
||||
width: min(1480px, 100%);
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.tarot-frame-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tarot-frame-title {
|
||||
margin: 0;
|
||||
font-size: clamp(28px, 4vw, 38px);
|
||||
line-height: 1.05;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.tarot-frame-copy {
|
||||
margin: 8px 0 0;
|
||||
max-width: 820px;
|
||||
color: #cbd5e1;
|
||||
font-size: 14px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.tarot-frame-actions {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tarot-frame-action-btn {
|
||||
padding: 10px 14px;
|
||||
border: 1px solid #4c1d95;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(180deg, #312e81, #1d1b4b);
|
||||
color: #eef2ff;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.tarot-frame-action-btn:hover {
|
||||
border-color: #6d28d9;
|
||||
background: linear-gradient(180deg, #4338ca, #312e81);
|
||||
}
|
||||
|
||||
.tarot-frame-settings-panel {
|
||||
position: absolute;
|
||||
top: calc(100% + 10px);
|
||||
right: 0;
|
||||
z-index: 25;
|
||||
min-width: 220px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
border: 1px solid #312e81;
|
||||
border-radius: 16px;
|
||||
background:
|
||||
radial-gradient(circle at top, rgba(99, 102, 241, 0.14), transparent 40%),
|
||||
linear-gradient(180deg, rgba(22, 22, 34, 0.98), rgba(10, 10, 18, 0.98));
|
||||
box-shadow: 0 18px 38px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.tarot-frame-settings-panel[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.tarot-frame-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 9px 10px;
|
||||
border: 1px solid rgba(99, 102, 241, 0.34);
|
||||
border-radius: 12px;
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
color: #e2e8f0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tarot-frame-toggle input {
|
||||
margin: 0;
|
||||
accent-color: #818cf8;
|
||||
}
|
||||
|
||||
.tarot-frame-toggle input:disabled,
|
||||
.tarot-frame-export-btn:disabled,
|
||||
.tarot-frame-settings-toggle:disabled {
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.tarot-frame-export-btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tarot-frame-status {
|
||||
padding: 10px 14px;
|
||||
border: 1px solid #27272a;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(180deg, rgba(20, 20, 32, 0.95), rgba(10, 10, 18, 0.95));
|
||||
color: #d4d4d8;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.tarot-frame-board-grid {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tarot-frame-panel {
|
||||
--frame-cell-size: clamp(34px, 3.1vw, 52px);
|
||||
--frame-gap: clamp(2px, 0.3vw, 6px);
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
padding: 18px;
|
||||
border: 1px solid #27272a;
|
||||
border-radius: 22px;
|
||||
background:
|
||||
radial-gradient(circle at top, rgba(59, 130, 246, 0.08), transparent 34%),
|
||||
linear-gradient(180deg, #161622 0%, #0f0f17 100%);
|
||||
box-shadow: 0 22px 54px rgba(0, 0, 0, 0.24);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tarot-frame-panel-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
min-width: max-content;
|
||||
}
|
||||
|
||||
.tarot-frame-panel-title {
|
||||
margin: 0;
|
||||
font-size: 17px;
|
||||
line-height: 1.2;
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
.tarot-frame-panel-subtitle {
|
||||
margin: 5px 0 0;
|
||||
color: #94a3b8;
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.tarot-frame-panel-count {
|
||||
align-self: center;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: rgba(15, 23, 42, 0.78);
|
||||
color: #cbd5e1;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tarot-frame-legend {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tarot-frame-legend-item {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(71, 85, 105, 0.56);
|
||||
border-radius: 14px;
|
||||
background: rgba(15, 23, 42, 0.46);
|
||||
color: #cbd5e1;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.tarot-frame-legend-item strong {
|
||||
color: #f8fafc;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.tarot-frame-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--frame-grid-size), var(--frame-cell-size));
|
||||
grid-template-rows: repeat(var(--frame-grid-size), var(--frame-cell-size));
|
||||
gap: var(--frame-gap);
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
min-width: max-content;
|
||||
}
|
||||
|
||||
.tarot-frame-slot {
|
||||
position: relative;
|
||||
width: var(--frame-cell-size);
|
||||
height: var(--frame-cell-size);
|
||||
border-radius: 8px;
|
||||
transition: transform 120ms ease, box-shadow 120ms ease, border-color 120ms ease;
|
||||
}
|
||||
|
||||
.tarot-frame-slot.is-empty-slot {
|
||||
border: 1px dashed rgba(148, 163, 184, 0.4);
|
||||
}
|
||||
|
||||
.tarot-frame-slot.is-drop-target {
|
||||
box-shadow: 0 0 0 2px #f59e0b, 0 0 0 6px rgba(245, 158, 11, 0.18);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.tarot-frame-slot.is-drag-source {
|
||||
opacity: 0.42;
|
||||
}
|
||||
|
||||
.tarot-frame-slot-empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: transparent;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.tarot-frame-slot-empty::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.tarot-frame-card {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
overflow: visible;
|
||||
cursor: grab;
|
||||
box-shadow: none;
|
||||
transition: transform 120ms ease, filter 120ms ease;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tarot-frame-card.is-empty {
|
||||
cursor: default;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.tarot-frame-card:hover {
|
||||
transform: translateY(-2px);
|
||||
filter: drop-shadow(0 10px 18px rgba(15, 23, 42, 0.38));
|
||||
}
|
||||
|
||||
.tarot-frame-card.is-empty:hover {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.tarot-frame-card-image,
|
||||
.tarot-frame-card-fallback {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tarot-frame-card-image {
|
||||
object-fit: contain;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.tarot-frame-card-fallback {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
color: #f8fafc;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
background:
|
||||
radial-gradient(circle at top, rgba(99, 102, 241, 0.18), transparent 50%),
|
||||
linear-gradient(180deg, #1e1b4b 0%, #0f172a 100%);
|
||||
}
|
||||
|
||||
.tarot-frame-card-badge {
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
right: 4px;
|
||||
bottom: 4px;
|
||||
padding: 4px 5px;
|
||||
border-radius: 8px;
|
||||
background: rgba(2, 6, 23, 0.84);
|
||||
color: #f8fafc;
|
||||
font-size: clamp(8px, 0.72vw, 10px);
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
letter-spacing: 0.02em;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
|
||||
white-space: normal;
|
||||
box-sizing: border-box;
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tarot-frame-grid.is-info-hidden .tarot-frame-card-badge {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tarot-frame-drag-ghost {
|
||||
position: fixed;
|
||||
z-index: 120;
|
||||
width: 86px;
|
||||
height: 129px;
|
||||
pointer-events: none;
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #818cf8;
|
||||
background: linear-gradient(180deg, #18181b 0%, #09090b 100%);
|
||||
box-shadow: 0 20px 42px rgba(0, 0, 0, 0.38);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.tarot-frame-drag-ghost img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tarot-frame-drag-ghost-label {
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
right: 6px;
|
||||
bottom: 6px;
|
||||
padding: 4px 5px;
|
||||
border-radius: 999px;
|
||||
background: rgba(2, 6, 23, 0.88);
|
||||
color: #f8fafc;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body.is-tarot-frame-dragging {
|
||||
cursor: grabbing;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.tarot-frame-legend {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.tarot-frame-view {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.tarot-frame-actions {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tarot-frame-settings-panel {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.tarot-frame-panel {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.tarot-frame-panel {
|
||||
--frame-cell-size: 28px;
|
||||
}
|
||||
|
||||
.tarot-frame-card-badge {
|
||||
font-size: 7px;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.tarot-house-card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tarot-house-card-actions {
|
||||
@@ -857,6 +1290,28 @@
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.tarot-house-settings-panel {
|
||||
position: absolute;
|
||||
top: calc(100% + 10px);
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
width: min(560px, 100%);
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid rgba(82, 82, 91, 0.9);
|
||||
border-radius: 14px;
|
||||
background: rgba(9, 9, 11, 0.96);
|
||||
box-shadow: 0 18px 44px rgba(0, 0, 0, 0.34);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
.tarot-house-settings-panel[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tarot-house-toggle {
|
||||
@@ -2897,12 +3352,13 @@
|
||||
}
|
||||
|
||||
.tarot-house-card-actions {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.tarot-house-settings-toggle,
|
||||
.tarot-house-toggle,
|
||||
.tarot-house-filter-group,
|
||||
.tarot-house-action-btn {
|
||||
@@ -2910,6 +3366,12 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tarot-house-settings-panel {
|
||||
position: static;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.tarot-house-filter-group {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
@@ -455,11 +455,13 @@
|
||||
const topbarEl = document.querySelector(".topbar");
|
||||
const actionsEl = document.getElementById("topbar-actions");
|
||||
const menuToggleEl = document.getElementById("topbar-menu-toggle");
|
||||
const settingsToggleEl = document.getElementById("open-settings");
|
||||
|
||||
return {
|
||||
topbarEl: topbarEl instanceof HTMLElement ? topbarEl : null,
|
||||
actionsEl: actionsEl instanceof HTMLElement ? actionsEl : null,
|
||||
menuToggleEl: menuToggleEl instanceof HTMLButtonElement ? menuToggleEl : null
|
||||
menuToggleEl: menuToggleEl instanceof HTMLButtonElement ? menuToggleEl : null,
|
||||
settingsToggleEl: settingsToggleEl instanceof HTMLButtonElement ? settingsToggleEl : null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -482,7 +484,7 @@
|
||||
}
|
||||
|
||||
function bindTopbarMobileMenu() {
|
||||
const { topbarEl, actionsEl, menuToggleEl } = getTopbarElements();
|
||||
const { topbarEl, actionsEl, menuToggleEl, settingsToggleEl } = getTopbarElements();
|
||||
if (!(topbarEl instanceof HTMLElement) || !(actionsEl instanceof HTMLElement) || !(menuToggleEl instanceof HTMLButtonElement)) {
|
||||
return;
|
||||
}
|
||||
@@ -500,6 +502,13 @@
|
||||
setTopbarMenuOpen(nextOpen);
|
||||
});
|
||||
|
||||
if (settingsToggleEl instanceof HTMLButtonElement && settingsToggleEl.dataset.topbarSettingsReady !== "1") {
|
||||
settingsToggleEl.dataset.topbarSettingsReady = "1";
|
||||
settingsToggleEl.addEventListener("click", () => {
|
||||
setTopbarMenuOpen(false);
|
||||
});
|
||||
}
|
||||
|
||||
actionsEl.addEventListener("click", (event) => {
|
||||
const button = event.target instanceof Element
|
||||
? event.target.closest("button")
|
||||
|
||||
@@ -22,6 +22,81 @@
|
||||
return config.getMagickDataset?.() || null;
|
||||
}
|
||||
|
||||
const DETAIL_VIEW_SELECTOR_BY_SECTION = {
|
||||
tarot: "#tarot-browse-view .tarot-layout",
|
||||
cube: "#cube-layout",
|
||||
zodiac: "#zodiac-section .planet-layout",
|
||||
"alphabet-letters": "#alphabet-letters-section .planet-layout",
|
||||
numbers: "#numbers-section .numbers-main-layout",
|
||||
iching: "#iching-section .planet-layout",
|
||||
gods: "#gods-section .planet-layout",
|
||||
calendar: "#calendar-section .planet-layout",
|
||||
"kabbalah-tree": "#kabbalah-tree-section .kab-layout",
|
||||
planets: "#planet-section .planet-layout",
|
||||
elements: "#elements-section .planet-layout"
|
||||
};
|
||||
|
||||
function showSectionDetailOnly(sectionKey, persist = false) {
|
||||
const selector = DETAIL_VIEW_SELECTOR_BY_SECTION[sectionKey];
|
||||
if (!selector) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const target = document.querySelector(selector);
|
||||
if (!(target instanceof HTMLElement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Boolean(window.TarotChromeUi?.showDetailOnly?.(target, persist));
|
||||
}
|
||||
|
||||
function isSectionDetailOnly(sectionKey) {
|
||||
const selector = DETAIL_VIEW_SELECTOR_BY_SECTION[sectionKey];
|
||||
if (!selector) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const target = document.querySelector(selector);
|
||||
if (!(target instanceof HTMLElement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return target.classList.contains("layout-sidebar-collapsed")
|
||||
&& !target.classList.contains("layout-detail-collapsed");
|
||||
}
|
||||
|
||||
function scheduleSectionDetailOnly(sectionKey, persist = false, attempts = 4) {
|
||||
requestAnimationFrame(() => {
|
||||
showSectionDetailOnly(sectionKey, persist);
|
||||
if (!isSectionDetailOnly(sectionKey) && attempts > 1) {
|
||||
scheduleSectionDetailOnly(sectionKey, persist, attempts - 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function prepareTarotBrowseDetailView() {
|
||||
const ensure = config.ensure || {};
|
||||
const referenceData = getReferenceData();
|
||||
const magickDataset = getMagickDataset();
|
||||
|
||||
setActiveSection("tarot");
|
||||
config.tarotSpreadUi?.showCardsView?.();
|
||||
|
||||
if (typeof ensure.ensureTarotSection === "function" && referenceData) {
|
||||
await ensure.ensureTarotSection(referenceData, magickDataset);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => {
|
||||
requestAnimationFrame(resolve);
|
||||
});
|
||||
|
||||
await new Promise((resolve) => {
|
||||
requestAnimationFrame(resolve);
|
||||
});
|
||||
|
||||
showSectionDetailOnly("tarot");
|
||||
}
|
||||
|
||||
function bindClick(element, handler) {
|
||||
if (!element) {
|
||||
return;
|
||||
@@ -60,6 +135,10 @@
|
||||
}
|
||||
});
|
||||
|
||||
bindClick(elements.openTarotFrameEl, () => {
|
||||
setActiveSection(getActiveSection() === "tarot-frame" ? "home" : "tarot-frame");
|
||||
});
|
||||
|
||||
bindClick(elements.openTarotHouseEl, () => {
|
||||
setActiveSection(getActiveSection() === "tarot-house" ? "home" : "tarot-house");
|
||||
});
|
||||
@@ -170,6 +249,7 @@
|
||||
if (!selected && detail?.wallId) {
|
||||
ui?.selectWallById?.(detail.wallId);
|
||||
}
|
||||
scheduleSectionDetailOnly("cube");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -184,6 +264,7 @@
|
||||
if (signId) {
|
||||
requestAnimationFrame(() => {
|
||||
window.ZodiacSectionUi?.selectBySignId?.(signId);
|
||||
scheduleSectionDetailOnly("zodiac");
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -207,22 +288,27 @@
|
||||
const ui = window.AlphabetSectionUi;
|
||||
if ((alphabet === "hebrew" || (!alphabet && hebrewLetterId)) && hebrewLetterId) {
|
||||
ui?.selectLetterByHebrewId?.(hebrewLetterId);
|
||||
scheduleSectionDetailOnly("alphabet-letters");
|
||||
return;
|
||||
}
|
||||
if (alphabet === "greek" && greekName) {
|
||||
ui?.selectGreekLetterByName?.(greekName);
|
||||
scheduleSectionDetailOnly("alphabet-letters");
|
||||
return;
|
||||
}
|
||||
if (alphabet === "english" && englishLetter) {
|
||||
ui?.selectEnglishLetter?.(englishLetter);
|
||||
scheduleSectionDetailOnly("alphabet-letters");
|
||||
return;
|
||||
}
|
||||
if (alphabet === "arabic" && arabicName) {
|
||||
ui?.selectArabicLetter?.(arabicName);
|
||||
scheduleSectionDetailOnly("alphabet-letters");
|
||||
return;
|
||||
}
|
||||
if (alphabet === "enochian" && enochianId) {
|
||||
ui?.selectEnochianLetter?.(enochianId);
|
||||
scheduleSectionDetailOnly("alphabet-letters");
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -241,6 +327,7 @@
|
||||
if (typeof config.selectNumberEntry === "function") {
|
||||
config.selectNumberEntry(normalizedValue);
|
||||
}
|
||||
scheduleSectionDetailOnly("numbers");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -259,10 +346,12 @@
|
||||
const ui = window.IChingSectionUi;
|
||||
if (hexagramNumber != null) {
|
||||
ui?.selectByHexagramNumber?.(hexagramNumber);
|
||||
scheduleSectionDetailOnly("iching");
|
||||
return;
|
||||
}
|
||||
if (planetaryInfluence) {
|
||||
ui?.selectByPlanetaryInfluence?.(planetaryInfluence);
|
||||
scheduleSectionDetailOnly("iching");
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -284,6 +373,7 @@
|
||||
if (!viaId && !viaName && pathNo != null) {
|
||||
ui?.selectByPathNo?.(pathNo);
|
||||
}
|
||||
scheduleSectionDetailOnly("gods");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -307,6 +397,7 @@
|
||||
window.CalendarSectionUi?.selectCalendarType?.(calendarId);
|
||||
}
|
||||
window.CalendarSectionUi?.selectByMonthId?.(monthId);
|
||||
scheduleSectionDetailOnly("calendar");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -320,6 +411,7 @@
|
||||
if (pathNo != null) {
|
||||
requestAnimationFrame(() => {
|
||||
window.KabbalahSectionUi?.selectNode?.(pathNo);
|
||||
scheduleSectionDetailOnly("kabbalah-tree");
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -337,6 +429,7 @@
|
||||
setActiveSection("planets");
|
||||
requestAnimationFrame(() => {
|
||||
window.PlanetSectionUi?.selectByPlanetId?.(planetId);
|
||||
scheduleSectionDetailOnly("planets");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -355,38 +448,27 @@
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
window.ElementsSectionUi?.selectByElementId?.(elementId);
|
||||
scheduleSectionDetailOnly("elements");
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("nav:tarot-trump", (event) => {
|
||||
const referenceData = getReferenceData();
|
||||
const magickDataset = getMagickDataset();
|
||||
if (typeof ensure.ensureTarotSection === "function" && referenceData) {
|
||||
ensure.ensureTarotSection(referenceData, magickDataset);
|
||||
}
|
||||
setActiveSection("tarot");
|
||||
document.addEventListener("nav:tarot-trump", async (event) => {
|
||||
await prepareTarotBrowseDetailView();
|
||||
const { trumpNumber, cardName } = event?.detail || {};
|
||||
requestAnimationFrame(() => {
|
||||
if (trumpNumber != null) {
|
||||
window.TarotSectionUi?.selectCardByTrump?.(trumpNumber);
|
||||
} else if (cardName) {
|
||||
window.TarotSectionUi?.selectCardByName?.(cardName);
|
||||
}
|
||||
});
|
||||
|
||||
if (trumpNumber != null) {
|
||||
window.TarotSectionUi?.selectCardByTrump?.(trumpNumber);
|
||||
} else if (cardName) {
|
||||
window.TarotSectionUi?.selectCardByName?.(cardName);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("kab:view-trump", (event) => {
|
||||
const referenceData = getReferenceData();
|
||||
const magickDataset = getMagickDataset();
|
||||
setActiveSection("tarot");
|
||||
document.addEventListener("kab:view-trump", async (event) => {
|
||||
await prepareTarotBrowseDetailView();
|
||||
const trumpNumber = event?.detail?.trumpNumber;
|
||||
|
||||
if (trumpNumber != null) {
|
||||
if (typeof ensure.ensureTarotSection === "function" && referenceData) {
|
||||
ensure.ensureTarotSection(referenceData, magickDataset);
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
window.TarotSectionUi?.selectCardByTrump?.(trumpNumber);
|
||||
});
|
||||
window.TarotSectionUi?.selectCardByTrump?.(trumpNumber);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -401,6 +483,7 @@
|
||||
} else {
|
||||
kabbalahUi?.selectPathByNumber?.(pathNumber);
|
||||
}
|
||||
scheduleSectionDetailOnly("kabbalah-tree");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"audio-circle",
|
||||
"audio-notes",
|
||||
"tarot",
|
||||
"tarot-frame",
|
||||
"tarot-house",
|
||||
"astronomy",
|
||||
"planets",
|
||||
@@ -94,8 +95,9 @@
|
||||
const isAudioCircleOpen = activeSection === "audio-circle";
|
||||
const isAudioMenuOpen = isAudioNotesOpen || isAudioCircleOpen;
|
||||
const isTarotOpen = activeSection === "tarot";
|
||||
const isTarotFrameOpen = activeSection === "tarot-frame";
|
||||
const isTarotHouseOpen = activeSection === "tarot-house";
|
||||
const isTarotMenuOpen = isTarotOpen || isTarotHouseOpen;
|
||||
const isTarotMenuOpen = isTarotOpen || isTarotFrameOpen || isTarotHouseOpen;
|
||||
const isAstronomyOpen = activeSection === "astronomy";
|
||||
const isPlanetOpen = activeSection === "planets";
|
||||
const isCyclesOpen = activeSection === "cycles";
|
||||
@@ -123,6 +125,7 @@
|
||||
setHidden(elements.audioCircleSectionEl, !isAudioCircleOpen);
|
||||
setHidden(elements.audioNotesSectionEl, !isAudioNotesOpen);
|
||||
setHidden(elements.tarotSectionEl, !isTarotOpen);
|
||||
setHidden(elements.tarotFrameSectionEl, !isTarotFrameOpen);
|
||||
setHidden(elements.tarotHouseSectionEl, !isTarotHouseOpen);
|
||||
setHidden(elements.astronomySectionEl, !isAstronomyOpen);
|
||||
setHidden(elements.planetSectionEl, !isPlanetOpen);
|
||||
@@ -152,6 +155,7 @@
|
||||
toggleActive(elements.openAudioCircleEl, isAudioCircleOpen);
|
||||
toggleActive(elements.openAudioNotesEl, isAudioNotesOpen);
|
||||
setPressed(elements.openTarotEl, isTarotMenuOpen);
|
||||
toggleActive(elements.openTarotFrameEl, isTarotFrameOpen);
|
||||
toggleActive(elements.openTarotHouseEl, isTarotHouseOpen);
|
||||
config.tarotSpreadUi?.applyViewState?.();
|
||||
setPressed(elements.openAstronomyEl, isAstronomyMenuOpen);
|
||||
@@ -211,6 +215,11 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTarotFrameOpen) {
|
||||
ensure.ensureTarotFrameSection?.(referenceData, magickDataset);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTarotHouseOpen) {
|
||||
ensure.ensureTarotSection?.(referenceData, magickDataset);
|
||||
return;
|
||||
|
||||
1223
app/ui-tarot-frame.js
Normal file
1223
app/ui-tarot-frame.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -58,6 +58,7 @@
|
||||
getSelectedCardId: () => "",
|
||||
getHouseTopCardsVisible: () => true,
|
||||
getHouseTopInfoModes: () => ({}),
|
||||
getMagickDataset: () => null,
|
||||
getHouseBottomCardsVisible: () => true,
|
||||
getHouseBottomInfoModes: () => ({})
|
||||
};
|
||||
@@ -326,12 +327,33 @@
|
||||
const courtWindow = getFirstCardRelationByType(card, "courtDateWindow")?.data || null;
|
||||
const decan = getFirstCardRelationByType(card, "decan")?.data || null;
|
||||
const calendar = getFirstCardRelationByType(card, "calendarMonth")?.data || null;
|
||||
const zodiac = getFirstCardRelationByType(card, "zodiacCorrespondence")?.data
|
||||
|| getFirstCardRelationByType(card, "zodiac")?.data
|
||||
|| null;
|
||||
|
||||
const primary = normalizeLabelText(courtWindow?.dateRange || decan?.dateRange || calendar?.dateRange || calendar?.name);
|
||||
let zodiacDateRange = "";
|
||||
if (zodiac?.signId || zodiac?.id || zodiac?.name) {
|
||||
const zodiacId = normalizeLabelText(zodiac.signId || zodiac.id || zodiac.name).toLowerCase();
|
||||
const sign = config.getMagickDataset?.()?.grouped?.astrology?.zodiac?.[zodiacId] || null;
|
||||
const rulesFrom = Array.isArray(sign?.rulesFrom) ? sign.rulesFrom : [];
|
||||
if (rulesFrom.length === 2) {
|
||||
const [[startMonth, startDay], [endMonth, endDay]] = rulesFrom;
|
||||
const monthLabels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
const startLabel = Number.isFinite(Number(startMonth)) && Number.isFinite(Number(startDay))
|
||||
? `${monthLabels[Math.max(0, Math.min(11, Number(startMonth) - 1))]} ${Number(startDay)}`
|
||||
: "";
|
||||
const endLabel = Number.isFinite(Number(endMonth)) && Number.isFinite(Number(endDay))
|
||||
? `${monthLabels[Math.max(0, Math.min(11, Number(endMonth) - 1))]} ${Number(endDay)}`
|
||||
: "";
|
||||
zodiacDateRange = normalizeLabelText(startLabel && endLabel ? `${startLabel} - ${endLabel}` : startLabel || endLabel);
|
||||
}
|
||||
}
|
||||
|
||||
const primary = normalizeLabelText(courtWindow?.dateRange || decan?.dateRange || calendar?.dateRange || zodiacDateRange || calendar?.name);
|
||||
const secondary = normalizeLabelText(
|
||||
calendar?.name && primary !== calendar.name
|
||||
? calendar.name
|
||||
: decan?.signName
|
||||
: decan?.signName || zodiac?.name
|
||||
);
|
||||
|
||||
if (!primary) {
|
||||
@@ -442,6 +464,10 @@
|
||||
pushLine(buildPathNumberLabel(card)?.primary);
|
||||
}
|
||||
|
||||
if (getTopInfoModeEnabled("date")) {
|
||||
pushLine(buildDateLabel(card)?.primary);
|
||||
}
|
||||
|
||||
if (!lines.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
let overlayEl = null;
|
||||
let backdropEl = null;
|
||||
let toolbarEl = null;
|
||||
let settingsButtonEl = null;
|
||||
let settingsPanelEl = null;
|
||||
let helpButtonEl = null;
|
||||
let helpPanelEl = null;
|
||||
let compareButtonEl = null;
|
||||
@@ -79,6 +81,7 @@
|
||||
onSelectCardId: null,
|
||||
overlayOpacity: LIGHTBOX_COMPARE_DEFAULT_OVERLAY_OPACITY,
|
||||
zoomScale: LIGHTBOX_ZOOM_SCALE,
|
||||
settingsMenuOpen: false,
|
||||
helpOpen: false,
|
||||
primaryRotated: false,
|
||||
overlayRotated: false,
|
||||
@@ -350,6 +353,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
function closeSettingsMenu() {
|
||||
lightboxState.settingsMenuOpen = false;
|
||||
if (settingsPanelEl) {
|
||||
settingsPanelEl.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSettingsMenu() {
|
||||
if (!lightboxState.isOpen || zoomed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextOpen = !lightboxState.settingsMenuOpen;
|
||||
lightboxState.settingsMenuOpen = nextOpen;
|
||||
if (nextOpen) {
|
||||
lightboxState.helpOpen = false;
|
||||
closeDeckComparePanel();
|
||||
}
|
||||
applyComparePresentation();
|
||||
}
|
||||
|
||||
function suppressDeckCompareToggle(durationMs = 400) {
|
||||
suppressDeckCompareToggleUntil = Date.now() + Math.max(0, Number(durationMs) || 0);
|
||||
}
|
||||
@@ -417,6 +441,7 @@
|
||||
|
||||
function toggleDeckComparePanel() {
|
||||
if (!lightboxState.allowDeckCompare) {
|
||||
closeSettingsMenu();
|
||||
lightboxState.deckComparePickerOpen = true;
|
||||
lightboxState.deckCompareMessage = "Add another registered deck to use deck compare.";
|
||||
applyComparePresentation();
|
||||
@@ -426,6 +451,7 @@
|
||||
if (lightboxState.deckComparePickerOpen) {
|
||||
closeDeckComparePanel();
|
||||
} else {
|
||||
closeSettingsMenu();
|
||||
lightboxState.deckComparePickerOpen = true;
|
||||
}
|
||||
lightboxState.deckCompareMessage = lightboxState.availableCompareDecks.length
|
||||
@@ -798,13 +824,33 @@
|
||||
&& mobileInfoPanelEl
|
||||
&& mobileInfoPanelEl.style.display !== "none"
|
||||
);
|
||||
const settingsPanelVisible = Boolean(
|
||||
lightboxState.settingsMenuOpen
|
||||
&& settingsPanelEl
|
||||
&& settingsPanelEl.style.display !== "none"
|
||||
);
|
||||
const helpPanelVisible = Boolean(
|
||||
lightboxState.helpOpen
|
||||
&& helpPanelEl
|
||||
&& helpPanelEl.style.display !== "none"
|
||||
);
|
||||
const deckPickerVisible = Boolean(
|
||||
lightboxState.deckComparePickerOpen
|
||||
&& deckComparePanelEl
|
||||
&& deckComparePanelEl.style.display !== "none"
|
||||
);
|
||||
const toolbarHeight = toolbarEl instanceof HTMLElement && toolbarEl.style.display !== "none"
|
||||
? toolbarEl.offsetHeight
|
||||
: 0;
|
||||
const infoPanelHeight = mobileInfoPanelVisible && mobileInfoPanelEl instanceof HTMLElement
|
||||
? mobileInfoPanelEl.offsetHeight
|
||||
: 0;
|
||||
const bottomOffset = toolbarHeight + (mobileInfoPanelVisible ? infoPanelHeight + 32 : 24);
|
||||
const floatingPanelHeight = Math.max(
|
||||
settingsPanelVisible && settingsPanelEl instanceof HTMLElement ? settingsPanelEl.offsetHeight + 12 : 0,
|
||||
helpPanelVisible && helpPanelEl instanceof HTMLElement ? helpPanelEl.offsetHeight + 12 : 0,
|
||||
deckPickerVisible && deckComparePanelEl instanceof HTMLElement ? deckComparePanelEl.offsetHeight + 12 : 0
|
||||
);
|
||||
const bottomOffset = toolbarHeight + floatingPanelHeight + (mobileInfoPanelVisible ? infoPanelHeight + 32 : 24);
|
||||
|
||||
mobilePrevButtonEl.style.top = "auto";
|
||||
mobileNextButtonEl.style.top = "auto";
|
||||
@@ -902,6 +948,19 @@
|
||||
setOverlayOpacity(lightboxState.overlayOpacity);
|
||||
}
|
||||
|
||||
function syncSettingsUi() {
|
||||
if (!settingsButtonEl || !settingsPanelEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const canShow = lightboxState.isOpen && !zoomed;
|
||||
settingsButtonEl.style.display = canShow ? "inline-flex" : "none";
|
||||
settingsButtonEl.textContent = lightboxState.settingsMenuOpen ? "Hide Settings" : "Settings";
|
||||
settingsButtonEl.setAttribute("aria-expanded", canShow && lightboxState.settingsMenuOpen ? "true" : "false");
|
||||
settingsPanelEl.style.display = canShow && lightboxState.settingsMenuOpen ? "flex" : "none";
|
||||
settingsPanelEl.style.pointerEvents = canShow && lightboxState.settingsMenuOpen ? "auto" : "none";
|
||||
}
|
||||
|
||||
function syncDeckComparePicker() {
|
||||
if (!deckCompareButtonEl || !deckComparePanelEl || !deckCompareMessageEl || !deckCompareDeckListEl) {
|
||||
return;
|
||||
@@ -1077,21 +1136,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const isCompact = isCompactLightboxLayout();
|
||||
if (isCompact) {
|
||||
if (helpButtonEl.parentElement !== toolbarEl) {
|
||||
toolbarEl.insertBefore(helpButtonEl, zoomControlEl || null);
|
||||
}
|
||||
helpButtonEl.style.position = "static";
|
||||
helpButtonEl.style.zIndex = "auto";
|
||||
} else {
|
||||
if (helpButtonEl.parentElement !== overlayEl) {
|
||||
overlayEl.appendChild(helpButtonEl);
|
||||
}
|
||||
helpButtonEl.style.position = "fixed";
|
||||
helpButtonEl.style.zIndex = "2";
|
||||
}
|
||||
|
||||
const canShow = lightboxState.isOpen && !zoomed;
|
||||
helpButtonEl.style.display = canShow ? "inline-flex" : "none";
|
||||
helpPanelEl.style.display = canShow && lightboxState.helpOpen ? "flex" : "none";
|
||||
@@ -1121,18 +1165,22 @@
|
||||
const isCompact = isCompactLightboxLayout();
|
||||
|
||||
if (!isCompact) {
|
||||
helpButtonEl.style.right = "auto";
|
||||
helpButtonEl.style.top = "24px";
|
||||
helpButtonEl.style.left = "24px";
|
||||
settingsPanelEl.style.top = "72px";
|
||||
settingsPanelEl.style.right = "24px";
|
||||
settingsPanelEl.style.bottom = "auto";
|
||||
settingsPanelEl.style.left = "auto";
|
||||
settingsPanelEl.style.width = "min(320px, calc(100vw - 48px))";
|
||||
settingsPanelEl.style.maxHeight = "none";
|
||||
settingsPanelEl.style.overflowY = "visible";
|
||||
helpPanelEl.style.top = "72px";
|
||||
helpPanelEl.style.right = "auto";
|
||||
helpPanelEl.style.right = "24px";
|
||||
helpPanelEl.style.bottom = "auto";
|
||||
helpPanelEl.style.left = "24px";
|
||||
helpPanelEl.style.left = "auto";
|
||||
helpPanelEl.style.width = "min(320px, calc(100vw - 48px))";
|
||||
helpPanelEl.style.maxHeight = "none";
|
||||
helpPanelEl.style.overflowY = "visible";
|
||||
deckComparePanelEl.style.top = "24px";
|
||||
deckComparePanelEl.style.right = "176px";
|
||||
deckComparePanelEl.style.top = "72px";
|
||||
deckComparePanelEl.style.right = "24px";
|
||||
deckComparePanelEl.style.bottom = "auto";
|
||||
deckComparePanelEl.style.left = "auto";
|
||||
deckComparePanelEl.style.width = "min(280px, calc(100vw - 48px))";
|
||||
@@ -1145,6 +1193,7 @@
|
||||
|| !lightboxState.allowOverlayCompare
|
||||
|| (!isCompact && lightboxState.compareMode && !hasSecondaryCard());
|
||||
compareButtonEl.textContent = lightboxState.compareMode ? "Done Overlay" : "Overlay";
|
||||
syncSettingsUi();
|
||||
syncHelpUi();
|
||||
syncZoomControl();
|
||||
syncOpacityControl();
|
||||
@@ -1205,10 +1254,13 @@
|
||||
toolbarEl.style.flexWrap = "wrap";
|
||||
toolbarEl.style.alignItems = "center";
|
||||
toolbarEl.style.justifyContent = "center";
|
||||
helpButtonEl.style.top = "auto";
|
||||
helpButtonEl.style.right = "auto";
|
||||
helpButtonEl.style.bottom = "auto";
|
||||
helpButtonEl.style.left = "auto";
|
||||
settingsPanelEl.style.top = "auto";
|
||||
settingsPanelEl.style.right = "12px";
|
||||
settingsPanelEl.style.bottom = "calc(72px + env(safe-area-inset-bottom, 0px))";
|
||||
settingsPanelEl.style.left = "12px";
|
||||
settingsPanelEl.style.width = "auto";
|
||||
settingsPanelEl.style.maxHeight = "min(56svh, 440px)";
|
||||
settingsPanelEl.style.overflowY = "auto";
|
||||
helpPanelEl.style.top = "auto";
|
||||
helpPanelEl.style.right = "12px";
|
||||
helpPanelEl.style.bottom = "calc(72px + env(safe-area-inset-bottom, 0px))";
|
||||
@@ -1540,15 +1592,28 @@
|
||||
overlayEl.style.pointerEvents = "none";
|
||||
overlayEl.style.overscrollBehavior = "contain";
|
||||
|
||||
settingsButtonEl = document.createElement("button");
|
||||
settingsButtonEl.type = "button";
|
||||
settingsButtonEl.textContent = "Settings";
|
||||
settingsButtonEl.style.display = "none";
|
||||
settingsButtonEl.style.alignItems = "center";
|
||||
settingsButtonEl.style.justifyContent = "center";
|
||||
settingsButtonEl.style.border = "1px solid rgba(255, 255, 255, 0.2)";
|
||||
settingsButtonEl.style.background = "rgba(15, 23, 42, 0.84)";
|
||||
settingsButtonEl.style.color = "#f8fafc";
|
||||
settingsButtonEl.style.borderRadius = "999px";
|
||||
settingsButtonEl.style.padding = "10px 14px";
|
||||
settingsButtonEl.style.font = "600 13px/1.1 sans-serif";
|
||||
settingsButtonEl.style.cursor = "pointer";
|
||||
settingsButtonEl.style.backdropFilter = "blur(12px)";
|
||||
|
||||
helpButtonEl = document.createElement("button");
|
||||
helpButtonEl.type = "button";
|
||||
helpButtonEl.textContent = "Help";
|
||||
helpButtonEl.style.position = "fixed";
|
||||
helpButtonEl.style.top = "24px";
|
||||
helpButtonEl.style.left = "24px";
|
||||
helpButtonEl.style.display = "none";
|
||||
helpButtonEl.style.alignItems = "center";
|
||||
helpButtonEl.style.justifyContent = "center";
|
||||
helpButtonEl.style.width = "100%";
|
||||
helpButtonEl.style.border = "1px solid rgba(255, 255, 255, 0.2)";
|
||||
helpButtonEl.style.background = "rgba(15, 23, 42, 0.84)";
|
||||
helpButtonEl.style.color = "#f8fafc";
|
||||
@@ -1557,8 +1622,28 @@
|
||||
helpButtonEl.style.font = "600 13px/1.1 sans-serif";
|
||||
helpButtonEl.style.cursor = "pointer";
|
||||
helpButtonEl.style.backdropFilter = "blur(12px)";
|
||||
helpButtonEl.style.pointerEvents = "auto";
|
||||
helpButtonEl.style.zIndex = "2";
|
||||
|
||||
settingsPanelEl = document.createElement("div");
|
||||
settingsPanelEl.style.position = "fixed";
|
||||
settingsPanelEl.style.top = "72px";
|
||||
settingsPanelEl.style.right = "24px";
|
||||
settingsPanelEl.style.display = "none";
|
||||
settingsPanelEl.style.flexDirection = "column";
|
||||
settingsPanelEl.style.gap = "10px";
|
||||
settingsPanelEl.style.width = "min(320px, calc(100vw - 48px))";
|
||||
settingsPanelEl.style.padding = "14px 16px";
|
||||
settingsPanelEl.style.borderRadius = "18px";
|
||||
settingsPanelEl.style.background = "rgba(2, 6, 23, 0.88)";
|
||||
settingsPanelEl.style.border = "1px solid rgba(148, 163, 184, 0.16)";
|
||||
settingsPanelEl.style.color = "#f8fafc";
|
||||
settingsPanelEl.style.boxShadow = "0 16px 42px rgba(0, 0, 0, 0.34)";
|
||||
settingsPanelEl.style.backdropFilter = "blur(12px)";
|
||||
settingsPanelEl.style.pointerEvents = "auto";
|
||||
settingsPanelEl.style.zIndex = "3";
|
||||
|
||||
const settingsTitleEl = document.createElement("div");
|
||||
settingsTitleEl.textContent = "Lightbox Settings";
|
||||
settingsTitleEl.style.font = "700 13px/1.3 sans-serif";
|
||||
|
||||
helpPanelEl = document.createElement("div");
|
||||
helpPanelEl.style.position = "fixed";
|
||||
@@ -1640,6 +1725,10 @@
|
||||
compareButtonEl.style.font = "600 13px/1.1 sans-serif";
|
||||
compareButtonEl.style.cursor = "pointer";
|
||||
compareButtonEl.style.backdropFilter = "blur(12px)";
|
||||
compareButtonEl.style.display = "inline-flex";
|
||||
compareButtonEl.style.alignItems = "center";
|
||||
compareButtonEl.style.justifyContent = "center";
|
||||
compareButtonEl.style.width = "100%";
|
||||
|
||||
deckCompareButtonEl = document.createElement("button");
|
||||
deckCompareButtonEl.type = "button";
|
||||
@@ -1652,10 +1741,15 @@
|
||||
deckCompareButtonEl.style.font = "600 13px/1.1 sans-serif";
|
||||
deckCompareButtonEl.style.cursor = "pointer";
|
||||
deckCompareButtonEl.style.backdropFilter = "blur(12px)";
|
||||
deckCompareButtonEl.style.alignItems = "center";
|
||||
deckCompareButtonEl.style.justifyContent = "center";
|
||||
deckCompareButtonEl.style.width = "100%";
|
||||
|
||||
zoomControlEl = document.createElement("label");
|
||||
zoomControlEl.style.display = "flex";
|
||||
zoomControlEl.style.alignItems = "center";
|
||||
zoomControlEl.style.justifyContent = "space-between";
|
||||
zoomControlEl.style.width = "100%";
|
||||
zoomControlEl.style.gap = "8px";
|
||||
zoomControlEl.style.padding = "10px 14px";
|
||||
zoomControlEl.style.border = "1px solid rgba(255, 255, 255, 0.2)";
|
||||
@@ -1687,6 +1781,8 @@
|
||||
opacityControlEl = document.createElement("label");
|
||||
opacityControlEl.style.display = "none";
|
||||
opacityControlEl.style.alignItems = "center";
|
||||
opacityControlEl.style.justifyContent = "space-between";
|
||||
opacityControlEl.style.width = "100%";
|
||||
opacityControlEl.style.gap = "8px";
|
||||
opacityControlEl.style.padding = "10px 14px";
|
||||
opacityControlEl.style.border = "1px solid rgba(255, 255, 255, 0.2)";
|
||||
@@ -1780,6 +1876,9 @@
|
||||
mobileInfoButtonEl.style.font = "600 13px/1.1 sans-serif";
|
||||
mobileInfoButtonEl.style.cursor = "pointer";
|
||||
mobileInfoButtonEl.style.backdropFilter = "blur(12px)";
|
||||
mobileInfoButtonEl.style.alignItems = "center";
|
||||
mobileInfoButtonEl.style.justifyContent = "center";
|
||||
mobileInfoButtonEl.style.width = "100%";
|
||||
|
||||
mobileInfoPrimaryTabEl = document.createElement("button");
|
||||
mobileInfoPrimaryTabEl.type = "button";
|
||||
@@ -1793,6 +1892,9 @@
|
||||
mobileInfoPrimaryTabEl.style.font = "600 13px/1.1 sans-serif";
|
||||
mobileInfoPrimaryTabEl.style.cursor = "pointer";
|
||||
mobileInfoPrimaryTabEl.style.backdropFilter = "blur(12px)";
|
||||
mobileInfoPrimaryTabEl.style.alignItems = "center";
|
||||
mobileInfoPrimaryTabEl.style.justifyContent = "center";
|
||||
mobileInfoPrimaryTabEl.style.width = "100%";
|
||||
|
||||
mobileInfoSecondaryTabEl = document.createElement("button");
|
||||
mobileInfoSecondaryTabEl.type = "button";
|
||||
@@ -1806,16 +1908,22 @@
|
||||
mobileInfoSecondaryTabEl.style.font = "600 13px/1.1 sans-serif";
|
||||
mobileInfoSecondaryTabEl.style.cursor = "pointer";
|
||||
mobileInfoSecondaryTabEl.style.backdropFilter = "blur(12px)";
|
||||
mobileInfoSecondaryTabEl.style.alignItems = "center";
|
||||
mobileInfoSecondaryTabEl.style.justifyContent = "center";
|
||||
mobileInfoSecondaryTabEl.style.width = "100%";
|
||||
|
||||
toolbarEl.append(
|
||||
settingsPanelEl.append(
|
||||
settingsTitleEl,
|
||||
compareButtonEl,
|
||||
deckCompareButtonEl,
|
||||
mobileInfoButtonEl,
|
||||
mobileInfoPrimaryTabEl,
|
||||
mobileInfoSecondaryTabEl,
|
||||
helpButtonEl,
|
||||
zoomControlEl,
|
||||
opacityControlEl
|
||||
);
|
||||
toolbarEl.append(settingsButtonEl);
|
||||
|
||||
stageEl = document.createElement("div");
|
||||
stageEl.style.position = "fixed";
|
||||
@@ -2106,7 +2214,7 @@
|
||||
overlayLayerEl.appendChild(overlayImageEl);
|
||||
frameEl.append(baseLayerEl, overlayLayerEl, mobileInfoPanelEl);
|
||||
stageEl.append(frameEl, compareGridEl, primaryInfoEl, secondaryInfoEl);
|
||||
overlayEl.append(backdropEl, stageEl, toolbarEl, deckComparePanelEl, helpButtonEl, helpPanelEl, mobilePrevButtonEl, mobileNextButtonEl);
|
||||
overlayEl.append(backdropEl, stageEl, toolbarEl, settingsPanelEl, deckComparePanelEl, helpPanelEl, mobilePrevButtonEl, mobileNextButtonEl);
|
||||
|
||||
const close = () => {
|
||||
if (!overlayEl || !imageEl || !overlayImageEl) {
|
||||
@@ -2134,6 +2242,7 @@
|
||||
lightboxState.onSelectCardId = null;
|
||||
lightboxState.overlayOpacity = LIGHTBOX_COMPARE_DEFAULT_OVERLAY_OPACITY;
|
||||
lightboxState.zoomScale = LIGHTBOX_ZOOM_SCALE;
|
||||
lightboxState.settingsMenuOpen = false;
|
||||
lightboxState.helpOpen = false;
|
||||
lightboxState.primaryRotated = false;
|
||||
lightboxState.overlayRotated = false;
|
||||
@@ -2169,8 +2278,6 @@
|
||||
lightboxState.compareMode = !lightboxState.compareMode;
|
||||
if (!lightboxState.compareMode) {
|
||||
clearSecondaryCard();
|
||||
} else if (isCompactLightboxLayout()) {
|
||||
lightboxState.mobileInfoOpen = true;
|
||||
}
|
||||
applyComparePresentation();
|
||||
}
|
||||
@@ -2311,9 +2418,24 @@
|
||||
backdropEl.addEventListener("click", close);
|
||||
helpButtonEl.addEventListener("click", () => {
|
||||
lightboxState.helpOpen = !lightboxState.helpOpen;
|
||||
if (lightboxState.helpOpen) {
|
||||
closeSettingsMenu();
|
||||
}
|
||||
syncHelpUi();
|
||||
restoreLightboxFocus();
|
||||
});
|
||||
settingsButtonEl.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleSettingsMenu();
|
||||
restoreLightboxFocus();
|
||||
});
|
||||
settingsPanelEl.addEventListener("pointerdown", (event) => {
|
||||
event.stopPropagation();
|
||||
});
|
||||
settingsPanelEl.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
});
|
||||
compareButtonEl.addEventListener("click", () => {
|
||||
toggleCompareMode();
|
||||
restoreLightboxFocus();
|
||||
@@ -2646,10 +2768,11 @@
|
||||
: null;
|
||||
lightboxState.overlayOpacity = LIGHTBOX_COMPARE_DEFAULT_OVERLAY_OPACITY;
|
||||
lightboxState.zoomScale = LIGHTBOX_ZOOM_SCALE;
|
||||
lightboxState.settingsMenuOpen = false;
|
||||
lightboxState.helpOpen = false;
|
||||
lightboxState.primaryRotated = false;
|
||||
lightboxState.overlayRotated = false;
|
||||
lightboxState.mobileInfoOpen = isCompactLightboxLayout();
|
||||
lightboxState.mobileInfoOpen = false;
|
||||
lightboxState.mobileInfoView = "primary";
|
||||
|
||||
imageEl.src = normalizedPrimary.src;
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
planet: true,
|
||||
zodiac: true,
|
||||
trump: true,
|
||||
path: true
|
||||
path: true,
|
||||
date: false
|
||||
},
|
||||
houseBottomCardsVisible: true,
|
||||
houseBottomInfoModes: {
|
||||
@@ -39,6 +40,7 @@
|
||||
},
|
||||
houseExportInProgress: false,
|
||||
houseExportFormat: "png",
|
||||
houseSettingsOpen: false,
|
||||
magickDataset: null,
|
||||
referenceData: null,
|
||||
monthRefsByCardId: new Map(),
|
||||
@@ -281,12 +283,15 @@
|
||||
tarotHouseOfCardsEl: document.getElementById("tarot-house-of-cards"),
|
||||
tarotBrowseViewEl: document.getElementById("tarot-browse-view"),
|
||||
tarotHouseViewEl: document.getElementById("tarot-house-view"),
|
||||
tarotHouseSettingsToggleEl: document.getElementById("tarot-house-settings-toggle"),
|
||||
tarotHouseSettingsPanelEl: document.getElementById("tarot-house-settings-panel"),
|
||||
tarotHouseTopCardsVisibleEl: document.getElementById("tarot-house-top-cards-visible"),
|
||||
tarotHouseTopInfoHebrewEl: document.getElementById("tarot-house-top-info-hebrew"),
|
||||
tarotHouseTopInfoPlanetEl: document.getElementById("tarot-house-top-info-planet"),
|
||||
tarotHouseTopInfoZodiacEl: document.getElementById("tarot-house-top-info-zodiac"),
|
||||
tarotHouseTopInfoTrumpEl: document.getElementById("tarot-house-top-info-trump"),
|
||||
tarotHouseTopInfoPathEl: document.getElementById("tarot-house-top-info-path"),
|
||||
tarotHouseTopInfoDateEl: document.getElementById("tarot-house-top-info-date"),
|
||||
tarotHouseBottomCardsVisibleEl: document.getElementById("tarot-house-bottom-cards-visible"),
|
||||
tarotHouseBottomInfoZodiacEl: document.getElementById("tarot-house-bottom-info-zodiac"),
|
||||
tarotHouseBottomInfoDecanEl: document.getElementById("tarot-house-bottom-info-decan"),
|
||||
@@ -544,6 +549,15 @@
|
||||
elements.tarotHouseViewEl.classList.toggle("is-house-focus", Boolean(state.houseFocusMode));
|
||||
}
|
||||
|
||||
if (elements?.tarotHouseSettingsToggleEl) {
|
||||
elements.tarotHouseSettingsToggleEl.setAttribute("aria-expanded", state.houseSettingsOpen ? "true" : "false");
|
||||
elements.tarotHouseSettingsToggleEl.textContent = state.houseSettingsOpen ? "Hide Settings" : "Settings";
|
||||
}
|
||||
|
||||
if (elements?.tarotHouseSettingsPanelEl) {
|
||||
elements.tarotHouseSettingsPanelEl.hidden = !state.houseSettingsOpen;
|
||||
}
|
||||
|
||||
if (elements?.tarotHouseTopCardsVisibleEl) {
|
||||
elements.tarotHouseTopCardsVisibleEl.checked = Boolean(state.houseTopCardsVisible);
|
||||
elements.tarotHouseTopCardsVisibleEl.disabled = Boolean(state.houseExportInProgress);
|
||||
@@ -554,6 +568,7 @@
|
||||
setHouseBottomInfoCheckboxState(elements?.tarotHouseTopInfoZodiacEl, state.houseTopInfoModes.zodiac);
|
||||
setHouseBottomInfoCheckboxState(elements?.tarotHouseTopInfoTrumpEl, state.houseTopInfoModes.trump);
|
||||
setHouseBottomInfoCheckboxState(elements?.tarotHouseTopInfoPathEl, state.houseTopInfoModes.path);
|
||||
setHouseBottomInfoCheckboxState(elements?.tarotHouseTopInfoDateEl, state.houseTopInfoModes.date);
|
||||
|
||||
if (elements?.tarotHouseBottomCardsVisibleEl) {
|
||||
elements.tarotHouseBottomCardsVisibleEl.checked = Boolean(state.houseBottomCardsVisible);
|
||||
@@ -839,6 +854,7 @@
|
||||
getSelectedCardId: () => state.selectedCardId,
|
||||
getHouseTopCardsVisible: () => state.houseTopCardsVisible,
|
||||
getHouseTopInfoModes: () => ({ ...state.houseTopInfoModes }),
|
||||
getMagickDataset: () => state.magickDataset,
|
||||
getHouseBottomCardsVisible: () => state.houseBottomCardsVisible,
|
||||
getHouseBottomInfoModes: () => ({ ...state.houseBottomInfoModes })
|
||||
});
|
||||
@@ -929,12 +945,47 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (elements.tarotHouseSettingsToggleEl) {
|
||||
elements.tarotHouseSettingsToggleEl.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
state.houseSettingsOpen = !state.houseSettingsOpen;
|
||||
syncHouseControls(elements);
|
||||
});
|
||||
}
|
||||
|
||||
if (elements.tarotHouseSettingsPanelEl) {
|
||||
elements.tarotHouseSettingsPanelEl.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
if (!state.houseSettingsOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = event.target;
|
||||
if (!(target instanceof Node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const settingsPanelEl = elements.tarotHouseSettingsPanelEl;
|
||||
const settingsToggleEl = elements.tarotHouseSettingsToggleEl;
|
||||
if (settingsPanelEl?.contains(target) || settingsToggleEl?.contains(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.houseSettingsOpen = false;
|
||||
syncHouseControls(elements);
|
||||
});
|
||||
|
||||
[
|
||||
[elements.tarotHouseTopInfoHebrewEl, "hebrew"],
|
||||
[elements.tarotHouseTopInfoPlanetEl, "planet"],
|
||||
[elements.tarotHouseTopInfoZodiacEl, "zodiac"],
|
||||
[elements.tarotHouseTopInfoTrumpEl, "trump"],
|
||||
[elements.tarotHouseTopInfoPathEl, "path"]
|
||||
[elements.tarotHouseTopInfoPathEl, "path"],
|
||||
[elements.tarotHouseTopInfoDateEl, "date"]
|
||||
].forEach(([checkbox, key]) => {
|
||||
if (!checkbox) {
|
||||
return;
|
||||
|
||||
53
index.html
53
index.html
@@ -16,12 +16,13 @@
|
||||
<link rel="stylesheet" href="node_modules/@fontsource/amiri/arabic-400.css">
|
||||
<link rel="stylesheet" href="node_modules/@fontsource/amiri/arabic-700.css">
|
||||
<link rel="stylesheet" href="node_modules/@fontsource/noto-naskh-arabic/arabic-400.css">
|
||||
<link rel="stylesheet" href="app/styles.css?v=20260328-house-mobile-01">
|
||||
<link rel="stylesheet" href="app/styles.css?v=20260401-tarot-frame-09">
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<button id="open-home" class="topbar-home-button" type="button" aria-pressed="true">Tarot Time!</button>
|
||||
<button id="topbar-menu-toggle" class="topbar-menu-toggle" type="button" aria-expanded="false" aria-controls="topbar-actions" aria-label="Open navigation menu">Menu</button>
|
||||
<button id="open-settings" class="topbar-menu-toggle topbar-settings-toggle" type="button" aria-haspopup="dialog" aria-expanded="false">Settings</button>
|
||||
<div id="topbar-actions" class="topbar-actions">
|
||||
<div class="topbar-dropdown" aria-label="Alphabet menu">
|
||||
<button id="open-alphabet" class="settings-trigger" type="button" aria-pressed="false" aria-haspopup="menu" aria-controls="alphabet-subpages" aria-expanded="false">Alphabet ▾</button>
|
||||
@@ -68,12 +69,12 @@
|
||||
</div>
|
||||
<button id="open-numbers" class="settings-trigger" type="button" aria-pressed="false">Numbers</button>
|
||||
<button id="open-quiz" class="settings-trigger" type="button" aria-pressed="false">Quiz</button>
|
||||
<button id="open-settings" class="settings-trigger" type="button" aria-haspopup="dialog" aria-expanded="false">Settings</button>
|
||||
<div class="topbar-dropdown" aria-label="Tarot menu">
|
||||
<button id="open-tarot" class="settings-trigger" type="button" aria-pressed="false" aria-haspopup="menu" aria-controls="tarot-subpages" aria-expanded="false">Tarot ▾</button>
|
||||
<div id="tarot-subpages" class="topbar-dropdown-menu" role="menu" aria-label="Tarot subpages">
|
||||
<button id="open-tarot-cards" class="settings-trigger topbar-sub-trigger" type="button" role="menuitem">Cards</button>
|
||||
<button id="open-tarot-spread" class="settings-trigger topbar-sub-trigger" type="button" role="menuitem">Draw Spread</button>
|
||||
<button id="open-tarot-frame" class="settings-trigger topbar-sub-trigger" type="button" role="menuitem">Frame</button>
|
||||
<button id="open-tarot-house" class="settings-trigger topbar-sub-trigger" type="button" role="menuitem">House</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -307,6 +308,31 @@
|
||||
<div id="tarot-spread-board" class="tarot-spread-board" aria-live="polite"></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="tarot-frame-section" hidden>
|
||||
<div id="tarot-frame-view" class="tarot-frame-view">
|
||||
<div class="tarot-frame-shell">
|
||||
<div class="tarot-frame-header">
|
||||
<div>
|
||||
<h2 class="tarot-frame-title">Tarot Frame</h2>
|
||||
<p class="tarot-frame-copy">Arrange all 78 tarot cards inside one master 18x18 grid. The extra cards sit across the top row, and every square stays available for custom layouts.</p>
|
||||
</div>
|
||||
<div class="tarot-frame-actions">
|
||||
<button id="tarot-frame-settings-toggle" class="tarot-frame-action-btn tarot-frame-settings-toggle" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="tarot-frame-settings-panel">Settings</button>
|
||||
<button id="tarot-frame-reset" class="tarot-frame-action-btn" type="button">Reset Layout</button>
|
||||
<div id="tarot-frame-settings-panel" class="tarot-frame-settings-panel" role="dialog" aria-label="Tarot Frame settings" hidden>
|
||||
<label class="tarot-frame-toggle" for="tarot-frame-show-info">
|
||||
<input id="tarot-frame-show-info" type="checkbox" checked>
|
||||
<span>Display Info</span>
|
||||
</label>
|
||||
<button id="tarot-frame-export-webp" class="tarot-frame-action-btn tarot-frame-export-btn" type="button">Export WebP</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tarot-frame-status" class="tarot-frame-status" aria-live="polite">Loading tarot cards...</div>
|
||||
<div id="tarot-frame-board" class="tarot-frame-board-grid"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="tarot-house-section" hidden>
|
||||
<div id="tarot-house-view" class="tarot-house-view">
|
||||
<section class="tarot-misc-section tarot-section-house-top tarot-section-house-page" aria-label="Tarot house of cards">
|
||||
@@ -314,6 +340,9 @@
|
||||
<div class="tarot-house-card-head">
|
||||
<strong>House of Cards</strong>
|
||||
<div class="tarot-house-card-actions">
|
||||
<button id="tarot-house-settings-toggle" class="tarot-house-action-btn tarot-house-settings-toggle" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="tarot-house-settings-panel">Settings</button>
|
||||
</div>
|
||||
<div id="tarot-house-settings-panel" class="tarot-house-settings-panel" role="dialog" aria-label="House of Cards settings" hidden>
|
||||
<label class="tarot-house-toggle" for="tarot-house-top-cards-visible">
|
||||
<input id="tarot-house-top-cards-visible" type="checkbox" checked>
|
||||
<span>Show Top Cards</span>
|
||||
@@ -340,6 +369,10 @@
|
||||
<input id="tarot-house-top-info-path" type="checkbox" checked>
|
||||
<span>Path</span>
|
||||
</label>
|
||||
<label class="tarot-house-mini-toggle" for="tarot-house-top-info-date">
|
||||
<input id="tarot-house-top-info-date" type="checkbox">
|
||||
<span>Date</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<label class="tarot-house-toggle" for="tarot-house-bottom-cards-visible">
|
||||
<input id="tarot-house-bottom-cards-visible" type="checkbox" checked>
|
||||
@@ -1037,8 +1070,8 @@
|
||||
<script src="app/data-service.js?v=20260319-word-dictionary-01"></script>
|
||||
<script src="app/calendar-events.js"></script>
|
||||
<script src="app/card-images.js?v=20260309-gate"></script>
|
||||
<script src="app/ui-tarot-lightbox.js?v=20260328-mobile-compare-02"></script>
|
||||
<script src="app/ui-tarot-house.js?v=20260328-house-mobile-01"></script>
|
||||
<script src="app/ui-tarot-lightbox.js?v=20260328-lightbox-settings-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-now-helpers.js?v=20260314-now-planets-grid-01"></script>
|
||||
<script src="app/ui-now.js?v=20260314-now-planets-grid-01"></script>
|
||||
@@ -1057,7 +1090,7 @@
|
||||
<script src="app/ui-tarot-card-derivations.js?v=20260307b"></script>
|
||||
<script src="app/ui-tarot-detail.js?v=20260307b"></script>
|
||||
<script src="app/ui-tarot-relation-display.js?v=20260307b"></script>
|
||||
<script src="app/ui-tarot.js?v=20260328-house-mobile-01"></script>
|
||||
<script src="app/ui-tarot.js?v=20260401-house-top-date-01"></script>
|
||||
<script src="app/ui-planets-references.js"></script>
|
||||
<script src="app/ui-planets.js"></script>
|
||||
<script src="app/ui-cycles.js"></script>
|
||||
@@ -1097,14 +1130,16 @@
|
||||
<script src="app/ui-numbers-detail.js"></script>
|
||||
<script src="app/ui-numbers.js"></script>
|
||||
<script src="app/ui-tarot-spread.js"></script>
|
||||
<script src="app/ui-tarot-frame.js?v=20260401-tarot-frame-09"></script>
|
||||
<script src="app/ui-settings.js?v=20260309-gate"></script>
|
||||
<script src="app/ui-chrome.js?v=20260314-topbar-panel-toggle-02"></script>
|
||||
<script src="app/ui-navigation.js?v=20260314-audio-circle-01"></script>
|
||||
<script src="app/ui-chrome.js?v=20260328-topbar-settings-01"></script>
|
||||
<script src="app/ui-navigation.js?v=20260401-tarot-frame-01"></script>
|
||||
<script src="app/ui-calendar-formatting.js?v=20260307b"></script>
|
||||
<script src="app/ui-calendar-visuals.js?v=20260307b"></script>
|
||||
<script src="app/ui-home-calendar.js"></script>
|
||||
<script src="app/ui-section-state.js?v=20260314-audio-circle-01"></script>
|
||||
<script src="app/ui-section-state.js?v=20260401-tarot-frame-01"></script>
|
||||
<script src="app/app-runtime.js?v=20260309-gate"></script>
|
||||
<script src="app.js?v=20260314-audio-circle-01"></script>
|
||||
<script src="app.js?v=20260401-tarot-frame-01"></script>
|
||||
<script src="app/navigation-detail-test-harness.js?v=20260401-universal-detail-02"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user