diff --git a/app/ui-cube.js b/app/ui-cube.js index 4d16f02..2bbb308 100644 --- a/app/ui-cube.js +++ b/app/ui-cube.js @@ -135,6 +135,7 @@ const cubeChassisUi = window.CubeChassisUi || {}; const cubeMathHelpers = window.CubeMathHelpers || {}; const cubeSelectionHelpers = window.CubeSelectionHelpers || {}; + let detailNavigator = null; let webpExportSupported = null; function getElements() { @@ -155,6 +156,9 @@ rotationReadoutEl: document.getElementById("cube-rotation-readout"), detailNameEl: document.getElementById("cube-detail-name"), detailSubEl: document.getElementById("cube-detail-sub"), + detailPrevEl: document.getElementById("cube-detail-prev"), + detailPositionEl: document.getElementById("cube-detail-position"), + detailNextEl: document.getElementById("cube-detail-next"), detailBodyEl: document.getElementById("cube-detail-body") }; } @@ -168,6 +172,7 @@ const aliases = { aleph: "alef", beth: "bet", + heh: "he", zain: "zayin", cheth: "het", chet: "het", @@ -968,6 +973,118 @@ }); } + function buildSequenceKey(nodeType, id) { + const normalizedNodeType = normalizeId(nodeType); + if (normalizedNodeType === "edge") { + const normalizedEdgeId = normalizeEdgeId(id); + return normalizedEdgeId ? `edge:${normalizedEdgeId}` : ""; + } + + if (normalizedNodeType === "wall") { + const normalizedWallId = normalizeId(id); + return normalizedWallId ? `wall:${normalizedWallId}` : ""; + } + + return ""; + } + + function getDetailSequenceState() { + const selectedNodeType = normalizeId(state.selectedNodeType); + const isEdgeSequence = selectedNodeType === "edge"; + const isWallSequence = selectedNodeType === "wall"; + + if (!isEdgeSequence && !isWallSequence) { + return { + total: 0, + currentIndex: -1, + previousId: "", + nextId: "" + }; + } + + const entries = isEdgeSequence + ? EDGE_ORDER + .map((edgeId) => getEdgeById(edgeId)) + .filter(Boolean) + : WALL_ORDER + .map((wallId) => getWallById(wallId)) + .filter(Boolean); + + const selectedKey = isEdgeSequence + ? buildSequenceKey("edge", state.selectedEdgeId) + : buildSequenceKey("wall", state.selectedWallId); + const keys = entries.map((entry) => buildSequenceKey(selectedNodeType, entry?.id)); + const currentIndex = keys.findIndex((key) => key === selectedKey); + + return { + total: entries.length, + currentIndex, + previousId: currentIndex > 0 ? keys[currentIndex - 1] : "", + nextId: currentIndex >= 0 && currentIndex < keys.length - 1 ? keys[currentIndex + 1] : "" + }; + } + + function selectSequenceTarget(targetKey) { + const [nodeType, rawId] = String(targetKey || "").split(":"); + const normalizedNodeType = normalizeId(nodeType); + + if (normalizedNodeType === "edge") { + return selectEdgeById(rawId); + } + + if (normalizedNodeType === "wall") { + return selectWallById(rawId); + } + + return false; + } + + function getDetailNavigator() { + if (detailNavigator || typeof window.TarotSequenceNav?.createSequenceNavigator !== "function") { + return detailNavigator; + } + + detailNavigator = window.TarotSequenceNav.createSequenceNavigator({ + getElements, + isActive: (elements) => Boolean(elements?.cubeSectionEl && elements.cubeSectionEl.hidden === false), + getSequenceState: getDetailSequenceState, + getPrevButton: (elements) => elements?.detailPrevEl, + getNextButton: (elements) => elements?.detailNextEl, + getPositionEl: (elements) => elements?.detailPositionEl, + formatPositionText: ({ total, currentIndex }) => { + const selectedNodeType = normalizeId(state.selectedNodeType); + const label = selectedNodeType === "edge" + ? "edges" + : (selectedNodeType === "wall" ? "walls" : "items"); + + if (total > 0 && currentIndex >= 0) { + return `${currentIndex + 1} of ${total} ${label}`; + } + + if (selectedNodeType === "connector") { + return "Connector"; + } + + if (selectedNodeType === "center") { + return "Primal Point"; + } + + return total > 0 ? `${total} ${label}` : "No sequence"; + }, + selectTarget: (targetKey) => selectSequenceTarget(targetKey) + }); + + return detailNavigator; + } + + function syncDetailNavigation(elements = getElements()) { + getDetailNavigator()?.sync(elements); + } + + function bindDetailNavigation(elements = getElements()) { + getDetailNavigator()?.bind(elements); + } + function render(elements) { syncFocusControls(elements); syncExportControls(elements); @@ -991,6 +1108,7 @@ const walls = getWalls(); renderFaceSvg(elements.viewContainerEl, walls); renderDetail(elements, walls); + syncDetailNavigation(elements); } function ensureCubeSection(magickDataset) { @@ -1037,6 +1155,7 @@ } bindRotationControls(elements); + bindDetailNavigation(elements); render(elements); state.initialized = true; diff --git a/app/ui-zodiac-references.js b/app/ui-zodiac-references.js index be1ec30..efa6139 100644 --- a/app/ui-zodiac-references.js +++ b/app/ui-zodiac-references.js @@ -132,6 +132,7 @@ const aliases = { aleph: "alef", beth: "bet", + heh: "he", zain: "zayin", cheth: "het", chet: "het", diff --git a/index.html b/index.html index 2de7237..a9ca3e6 100644 --- a/index.html +++ b/index.html @@ -1172,6 +1172,11 @@