update tarot frame settings UI

This commit is contained in:
2026-04-01 16:08:52 -07:00
parent a7d956cee8
commit efe5017740
7 changed files with 1216 additions and 67 deletions

View File

@@ -903,6 +903,57 @@
flex-wrap: wrap;
}
.tarot-frame-layout-panel {
position: absolute;
top: calc(100% + 10px);
right: 0;
z-index: 26;
min-width: 270px;
display: grid;
gap: 8px;
padding: 12px;
border: 1px solid #312e81;
border-radius: 16px;
background:
radial-gradient(circle at top, rgba(99, 102, 241, 0.16), transparent 42%),
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-layout-panel[hidden] {
display: none !important;
}
.tarot-frame-layout-option {
display: grid;
gap: 4px;
padding: 10px 12px;
border: 1px solid rgba(99, 102, 241, 0.28);
border-radius: 12px;
background: rgba(15, 23, 42, 0.5);
color: #cbd5e1;
text-align: left;
cursor: pointer;
}
.tarot-frame-layout-option:hover,
.tarot-frame-layout-option.is-active {
border-color: rgba(129, 140, 248, 0.85);
background: rgba(49, 46, 129, 0.44);
color: #f8fafc;
}
.tarot-frame-layout-option strong {
font-size: 13px;
letter-spacing: 0.03em;
text-transform: uppercase;
}
.tarot-frame-layout-option span {
font-size: 12px;
line-height: 1.35;
}
.tarot-frame-action-btn {
padding: 10px 14px;
border: 1px solid #4c1d95;
@@ -937,6 +988,67 @@
box-shadow: 0 18px 38px rgba(0, 0, 0, 0.3);
}
.tarot-frame-settings-group {
display: grid;
gap: 10px;
padding-top: 2px;
border-top: 1px solid rgba(99, 102, 241, 0.18);
}
.tarot-frame-settings-group[hidden] {
display: none !important;
}
.tarot-frame-settings-heading {
color: #f8fafc;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.tarot-frame-settings-subheading {
color: #cbd5e1;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.tarot-frame-settings-note {
color: #94a3b8;
font-size: 11px;
line-height: 1.45;
}
.tarot-frame-checkbox-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.tarot-frame-check {
display: inline-flex;
align-items: center;
gap: 8px;
min-width: 0;
padding: 8px 9px;
border: 1px solid rgba(99, 102, 241, 0.24);
border-radius: 10px;
background: rgba(15, 23, 42, 0.4);
color: #dbe4f0;
font-size: 12px;
line-height: 1.2;
cursor: pointer;
-webkit-user-select: none;
user-select: none;
}
.tarot-frame-check input {
margin: 0;
accent-color: #818cf8;
}
.tarot-frame-settings-panel[hidden] {
display: none !important;
}
@@ -963,6 +1075,8 @@
}
.tarot-frame-toggle input:disabled,
.tarot-frame-check input:disabled,
.tarot-frame-layout-option:disabled,
.tarot-frame-export-btn:disabled,
.tarot-frame-settings-toggle:disabled {
cursor: wait;
@@ -1171,6 +1285,54 @@
linear-gradient(180deg, #1e1b4b 0%, #0f172a 100%);
}
.tarot-frame-card-text-face {
width: 100%;
height: 100%;
display: grid;
align-content: center;
justify-items: center;
gap: 4px;
padding: 6px 5px;
box-sizing: border-box;
color: #fafafa;
text-align: center;
background:
radial-gradient(circle at top, rgba(99, 102, 241, 0.16), transparent 55%),
linear-gradient(180deg, rgba(30, 41, 59, 0.94), rgba(15, 23, 42, 0.94));
pointer-events: none;
-webkit-user-select: none;
user-select: none;
}
.tarot-frame-card-text-face.is-dense {
gap: 3px;
padding: 5px 4px;
}
.tarot-frame-card-text-face.is-top-hebrew .tarot-frame-card-text-primary {
font-size: clamp(11px, 1vw, 16px);
line-height: 1;
font-family: "Noto Sans Hebrew", "Segoe UI Symbol", sans-serif;
}
.tarot-frame-card-text-primary {
display: block;
font-size: clamp(7px, 0.78vw, 10px);
line-height: 1.15;
font-weight: 700;
letter-spacing: 0.02em;
overflow-wrap: anywhere;
}
.tarot-frame-card-text-secondary {
display: block;
color: rgba(250, 250, 250, 0.76);
font-size: clamp(6px, 0.7vw, 8px);
line-height: 1.2;
letter-spacing: 0.02em;
overflow-wrap: anywhere;
}
.tarot-frame-card-badge {
position: absolute;
left: 4px;
@@ -1262,6 +1424,11 @@
right: auto;
}
.tarot-frame-layout-panel {
left: 0;
right: auto;
}
.tarot-frame-panel {
padding: 14px;
}
@@ -1274,6 +1441,23 @@
font-size: 7px;
padding: 3px 4px;
}
.tarot-frame-card-text-face {
gap: 2px;
padding: 4px 3px;
}
.tarot-frame-card-text-face.is-top-hebrew .tarot-frame-card-text-primary {
font-size: 10px;
}
.tarot-frame-card-text-primary {
font-size: 6px;
}
.tarot-frame-card-text-secondary {
font-size: 5px;
}
}
.tarot-house-card-head {

View File

@@ -43,6 +43,40 @@
throw new Error("Tarot database assembly dependencies are incomplete");
}
function buildTokenDateRange(startToken, endToken) {
const parseToken = (value) => {
const match = String(value || "").trim().match(/^(\d{2})-(\d{2})$/);
if (!match) {
return null;
}
const month = Number(match[1]);
const day = Number(match[2]);
if (!Number.isFinite(month) || !Number.isFinite(day)) {
return null;
}
return new Date(2025, month - 1, day);
};
const start = parseToken(startToken);
const endBase = parseToken(endToken);
if (!start || !endBase) {
return null;
}
const wraps = endBase.getTime() < start.getTime();
const end = wraps ? new Date(2026, endBase.getMonth(), endBase.getDate()) : endBase;
return {
start,
end,
startToken: startToken || null,
endToken: endToken || null,
label: `${formatMonthDayLabel(start)}${formatMonthDayLabel(end)}`
};
}
function buildMajorCards(referenceData, magickDataset) {
const tarotDb = getTarotDbConfig(referenceData);
const dynamicRelations = buildMajorDynamicRelations(referenceData);
@@ -178,6 +212,10 @@
const windowDecans = windowDecanIds
.map((decanId) => decanById.get(decanId) || null)
.filter(Boolean);
const explicitWindowRange = buildTokenDateRange(
tarotDb.courtDateRanges?.[cardName]?.start,
tarotDb.courtDateRanges?.[cardName]?.end
);
const dynamicRelations = [];
const monthKeys = new Set();
@@ -255,9 +293,17 @@
if (windowDecans.length) {
const firstRange = windowDecans[0].dateRange;
const lastRange = windowDecans[windowDecans.length - 1].dateRange;
const windowLabel = firstRange && lastRange
? `${formatMonthDayLabel(firstRange.start)}${formatMonthDayLabel(lastRange.end)}`
: "--";
const fallbackWindowRange = firstRange && lastRange
? {
start: firstRange.start,
end: lastRange.end,
startToken: firstRange.startToken,
endToken: lastRange.endToken,
label: `${formatMonthDayLabel(firstRange.start)}${formatMonthDayLabel(lastRange.end)}`
}
: null;
const windowRange = explicitWindowRange || fallbackWindowRange;
const windowLabel = windowRange?.label || "--";
dynamicRelations.unshift(
createRelation(
@@ -265,8 +311,8 @@
`${rankKey}-${suitKey}`,
`Court date window: ${windowLabel}`,
{
dateStart: firstRange?.startToken || null,
dateEnd: lastRange?.endToken || null,
dateStart: windowRange?.startToken || null,
dateEnd: windowRange?.endToken || null,
dateRange: windowLabel,
decanIds: windowDecanIds
}

View File

@@ -29,7 +29,8 @@
suitInfo: hasDb && db.suitInfo && typeof db.suitInfo === "object" ? db.suitInfo : suitInfo,
rankInfo: hasDb && db.rankInfo && typeof db.rankInfo === "object" ? db.rankInfo : rankInfo,
courtInfo: hasDb && db.courtInfo && typeof db.courtInfo === "object" ? db.courtInfo : courtInfo,
courtDecanWindows: hasDb && db.courtDecanWindows && typeof db.courtDecanWindows === "object" ? db.courtDecanWindows : courtDecanWindows
courtDecanWindows: hasDb && db.courtDecanWindows && typeof db.courtDecanWindows === "object" ? db.courtDecanWindows : courtDecanWindows,
courtDateRanges: hasDb && db.courtDateRanges && typeof db.courtDateRanges === "object" ? db.courtDateRanges : {}
};
}
@@ -178,7 +179,36 @@
return { start, end };
}
function buildDecanDateRange(sign, decanIndex) {
function buildTokenDateRange(startToken, endToken) {
const start = monthDayToDate(startToken, 2025);
const endBase = monthDayToDate(endToken, 2025);
if (!start || !endBase) {
return null;
}
const wraps = endBase.getTime() < start.getTime();
const end = wraps ? monthDayToDate(endToken, 2026) : endBase;
if (!end) {
return null;
}
return {
start,
end,
startMonth: start.getMonth() + 1,
endMonth: end.getMonth() + 1,
startToken: formatMonthDayToken(start),
endToken: formatMonthDayToken(end),
label: `${formatMonthDayLabel(start)}${formatMonthDayLabel(end)}`
};
}
function buildDecanDateRange(sign, decanIndex, decan = null) {
const explicitRange = buildTokenDateRange(decan?.dateStart, decan?.dateEnd);
if (explicitRange) {
return explicitRange;
}
const bounds = getSignDateBounds(sign);
if (!bounds || !Number.isFinite(Number(decanIndex))) {
return null;
@@ -238,7 +268,7 @@
const startDegree = (index - 1) * 10;
const endDegree = startDegree + 10;
const dateRange = buildDecanDateRange(sign, index);
const dateRange = buildDecanDateRange(sign, index, decan);
return {
decan,

File diff suppressed because it is too large Load Diff

View File

@@ -594,6 +594,46 @@
}
}
function refreshHouseUi() {
if (!state.initialized) {
return;
}
const elements = getElements();
renderHouseOfCards(elements);
syncHouseControls(elements);
}
function setHouseTopCardsVisible(value) {
state.houseTopCardsVisible = Boolean(value);
refreshHouseUi();
}
function setHouseTopInfoMode(mode, value) {
const key = String(mode || "").trim();
if (!key || !Object.prototype.hasOwnProperty.call(state.houseTopInfoModes, key)) {
return;
}
state.houseTopInfoModes[key] = Boolean(value);
refreshHouseUi();
}
function setHouseBottomCardsVisible(value) {
state.houseBottomCardsVisible = Boolean(value);
refreshHouseUi();
}
function setHouseBottomInfoMode(mode, value) {
const key = String(mode || "").trim();
if (!key || !Object.prototype.hasOwnProperty.call(state.houseBottomInfoModes, key)) {
return;
}
state.houseBottomInfoModes[key] = Boolean(value);
refreshHouseUi();
}
async function exportHouseOfCards(elements, format = "png") {
if (state.houseExportInProgress) {
return;
@@ -1076,6 +1116,14 @@
ensureTarotSection,
selectCardByTrump,
selectCardByName,
getCards: () => state.cards
getCards: () => state.cards,
getHouseTopCardsVisible: () => state.houseTopCardsVisible,
getHouseTopInfoModes: () => ({ ...state.houseTopInfoModes }),
getHouseBottomCardsVisible: () => state.houseBottomCardsVisible,
getHouseBottomInfoModes: () => ({ ...state.houseBottomInfoModes }),
setHouseTopCardsVisible,
setHouseTopInfoMode,
setHouseBottomCardsVisible,
setHouseBottomInfoMode
};
})();