(function () { document.addEventListener("DOMContentLoaded", async () => { const tabButtons = Array.from(document.querySelectorAll("[data-editor-tab]")); const panels = Array.from(document.querySelectorAll("[data-editor-panel]")); const panelShell = document.querySelector(".editor-tab-panels"); const newFileBtn = document.getElementById("editor-new-file"); const editFileBtn = document.getElementById("editor-edit-file"); function setHeaderButtonsBusy(busy) { [newFileBtn, editFileBtn].forEach((btn) => { if (!btn) return; btn.disabled = !!busy; btn.classList.toggle("is-disabled", !!busy); btn.setAttribute("aria-disabled", busy ? "true" : "false"); }); } const newTypeBackdrop = document.getElementById("editor-new-type-backdrop"); const newTypeClose = document.getElementById("editor-new-type-close"); const seqModalBackdrop = document.getElementById("seq-modal-backdrop"); const mutModalBackdrop = document.getElementById("mut-modal-backdrop"); const deleteBackdrop = document.getElementById("editor-delete-backdrop"); const duplicateBackdrop = document.getElementById("editor-duplicate-backdrop"); const SIGN_UP_PATH = "/sign-up"; let activeEditorTab = "assets"; let editorSyncDepth = 0; let isMemberSignedIn = false; window.__viciActiveEditorTab = activeEditorTab; function syncEditorModalOffset() { const main = document.querySelector('.editor-main'); const left = main ? Math.max(0, Math.round(main.getBoundingClientRect().left)) : 220; document.documentElement.style.setProperty('--editor-sidebar-width', `${left}px`); } const EDITOR_NEW_SVG = ` `; const EDITOR_EDIT_SVG = ` `; const EDITOR_UPLOAD_SVG = ` `; const EDITOR_SAVE_SVG = ` `; function syncEditorSidebarOffset() { requestAnimationFrame(syncEditorModalOffset); setTimeout(syncEditorModalOffset, 320); } window.addEventListener("resize", syncEditorModalOffset); const sidebar = document.querySelector(".dashboard-sidebar"); const handle = document.querySelector(".sidebar-edge-toggle"); if (handle) { handle.addEventListener("click", () => { syncEditorSidebarOffset(); }); } if (sidebar) { const observer = new MutationObserver(() => { syncEditorSidebarOffset(); }); observer.observe(sidebar, { attributes: true, attributeFilter: ["class"] }); } syncEditorSidebarOffset(); function syncBodyScrollLock() { const open = Boolean( (newTypeBackdrop && !newTypeBackdrop.hidden) || (seqModalBackdrop && !seqModalBackdrop.hidden) || (mutModalBackdrop && !mutModalBackdrop.hidden) || (deleteBackdrop && !deleteBackdrop.hidden) || (duplicateBackdrop && !duplicateBackdrop.hidden) ); document.documentElement.classList.toggle("modal-open", open); document.body.classList.toggle("modal-open", open); } function setPanelOverlay(on, label = "Syncing") { if (!panelShell) return; let overlay = panelShell.querySelector(".editor-panel-overlay"); if (!overlay) { overlay = document.createElement("div"); overlay.className = "editor-panel-overlay"; overlay.innerHTML = `
Syncing
`; panelShell.appendChild(overlay); } const clean = String(label || "Syncing").replace(/\.*\s*$/, ""); const overlayLabel = overlay.querySelector(".label"); if (overlayLabel) { overlayLabel.textContent = clean; overlayLabel.setAttribute("data-ellipsis", ""); } overlay.classList.toggle("show", !!on); panelShell.classList.toggle("is-sync-locked", !!on); document.querySelector(".editor-tabs")?.classList.toggle("is-sync-locked", !!on); setHeaderButtonsBusy(!!on); } async function withEditorSync(label, run) { editorSyncDepth += 1; setPanelOverlay(true, label || "Syncing"); try { return await run(); } finally { editorSyncDepth = Math.max(0, editorSyncDepth - 1); if (!editorSyncDepth) setPanelOverlay(false); } } function redirectToSignUp() { window.location.href = SIGN_UP_PATH; } async function detectMemberSignIn() { try { const ms = window.$memberstackDom; if (!ms?.getCurrentMember) return false; const { data } = await ms.getCurrentMember(); return !!data?.id; } catch (_) { return false; } } function refreshHeaderButtons() { if (!newFileBtn || !editFileBtn) return; const hasSequenceState = !!window.ViciSequenceEditor?.hasSequenceWorkingState?.(); const hasStructureState = !!window.ViciStructureEditor?.hasStructureWorkingState?.(); if (activeEditorTab === "assets") { newFileBtn.innerHTML = `${EDITOR_NEW_SVG}New`; editFileBtn.innerHTML = `${EDITOR_UPLOAD_SVG}Upload`; return; } if (activeEditorTab === "sequence" && hasSequenceState) { newFileBtn.innerHTML = `${EDITOR_EDIT_SVG}Edit`; editFileBtn.innerHTML = `${EDITOR_SAVE_SVG}Save`; return; } if (activeEditorTab === "structure" && hasStructureState) { newFileBtn.innerHTML = `${EDITOR_EDIT_SVG}Edit`; editFileBtn.innerHTML = `${EDITOR_SAVE_SVG}Save`; return; } newFileBtn.innerHTML = `${EDITOR_NEW_SVG}New`; editFileBtn.innerHTML = `${EDITOR_UPLOAD_SVG}Upload`; } function selectEditorTab(target) { if (!target) return; if (!["assets", "sequence", "structure"].includes(target)) return; if (target === "assets" && !isMemberSignedIn) { target = "sequence"; } activeEditorTab = target; window.__viciActiveEditorTab = target; tabButtons.forEach((btn) => { const isActive = btn.getAttribute("data-editor-tab") === target; btn.classList.toggle("is-active", isActive); btn.setAttribute("aria-selected", isActive ? "true" : "false"); }); panels.forEach((panel) => { const isMatch = panel.getAttribute("data-editor-panel") === target; panel.classList.toggle("is-active", isMatch); }); if (window.location.hash !== `#${target}`) { history.replaceState(null, "", `#${target}`); } document.dispatchEvent( new CustomEvent("editor:tab-change", { detail: { tab: target } }) ); refreshHeaderButtons(); if (target === "assets" && isMemberSignedIn) { window.ViciSequenceEditor?.refreshAssetsForCurrentContext?.(); } } function showShellBackdrop(el) { if (!el) return; el.hidden = false; el.setAttribute("aria-hidden", "false"); requestAnimationFrame(() => { el.classList.add("is-open"); }); syncBodyScrollLock(); } function hideShellBackdrop(el) { if (!el) return; el.classList.remove("is-open"); el.setAttribute("aria-hidden", "true"); el.hidden = true; syncBodyScrollLock(); } function closeNewTypeModal() { hideShellBackdrop(newTypeBackdrop); } function openNewTypeModal() { showShellBackdrop(newTypeBackdrop); } tabButtons.forEach((btn) => { btn.addEventListener("click", (e) => { e.preventDefault(); selectEditorTab(btn.getAttribute("data-editor-tab")); }); }); window.addEventListener("hashchange", () => { const h = String(window.location.hash || "").replace(/^#/, "").toLowerCase(); if (["assets", "sequence", "structure"].includes(h)) { selectEditorTab(h); } }); newFileBtn?.addEventListener("click", async (e) => { e.preventDefault(); if (activeEditorTab === "assets") { openNewTypeModal(); return; } if (activeEditorTab === "structure") { const hasState = !!window.ViciStructureEditor?.hasStructureWorkingState?.(); if (hasState) { window.ViciStructureEditor?.openStructureModal?.(); } else { await window.ViciStructureEditor?.handleNewFileClick?.(); } refreshHeaderButtons(); return; } if (activeEditorTab !== "sequence") { window.showToast?.("error", `${activeEditorTab[0].toUpperCase()}${activeEditorTab.slice(1)} tools coming soon`); return; } const hasState = !!window.ViciSequenceEditor?.hasSequenceWorkingState?.(); if (hasState) { window.ViciSequenceEditor?.openSeqModal?.(); } else { await window.ViciSequenceEditor?.handleNewFileClick?.(); } refreshHeaderButtons(); }); editFileBtn?.addEventListener("click", async (e) => { e.preventDefault(); if (activeEditorTab === "assets") { openNewTypeModal(); return; } if (activeEditorTab === "structure") { const hasState = !!window.ViciStructureEditor?.hasStructureWorkingState?.(); if (hasState) { if (!isMemberSignedIn) { redirectToSignUp(); return; } await window.ViciStructureEditor?.saveAndClose?.(); } else { window.ViciStructureEditor?.openUploadDialog?.(); } refreshHeaderButtons(); return; } if (activeEditorTab !== "sequence") { window.showToast?.("error", `${activeEditorTab[0].toUpperCase()}${activeEditorTab.slice(1)} tools coming soon`); return; } const hasState = !!window.ViciSequenceEditor?.hasSequenceWorkingState?.(); if (hasState) { if (!isMemberSignedIn) { redirectToSignUp(); return; } await window.ViciSequenceEditor?.saveAndClose?.(); } else { window.ViciSequenceEditor?.openSeqModal?.(); } refreshHeaderButtons(); }); document.querySelectorAll("[data-new-type]").forEach((btn) => { btn.addEventListener("click", () => { const type = btn.getAttribute("data-new-type") || "sequence"; closeNewTypeModal(); if (type === "sequence") { selectEditorTab("sequence"); window.ViciSequenceEditor?.openSeqModal?.(); return; } if (type === "structure") { selectEditorTab("structure"); requestAnimationFrame(() => { window.ViciStructureEditor?.openStructureModal?.(); }); return; } selectEditorTab(type); }); }); newTypeClose?.addEventListener("click", closeNewTypeModal); newTypeBackdrop?.addEventListener("click", (e) => { if (e.target === newTypeBackdrop) closeNewTypeModal(); }); document.addEventListener("keydown", (e) => { if (e.key !== "Escape") return; if (newTypeBackdrop && !newTypeBackdrop.hidden) closeNewTypeModal(); }); isMemberSignedIn = await detectMemberSignIn(); document.dispatchEvent( new CustomEvent("editor:auth-change", { detail: { signedIn: isMemberSignedIn } }) ); const hashTarget = String(window.location.hash || "").replace(/^#/, "").toLowerCase(); const fallbackTab = isMemberSignedIn ? "assets" : "sequence"; const desiredTab = ["assets", "sequence", "structure"].includes(hashTarget) ? hashTarget : fallbackTab; selectEditorTab(desiredTab); document.addEventListener("editor:sequence-state-change", refreshHeaderButtons); document.addEventListener("editor:structure-state-change", refreshHeaderButtons); document.addEventListener("editor:tab-change", refreshHeaderButtons); document.addEventListener("editor:auth-change", refreshHeaderButtons); refreshHeaderButtons(); window.ViciEditorShell = { getActiveTab: () => activeEditorTab, isMemberSignedIn: () => isMemberSignedIn, redirectToSignUp, setPanelOverlay, withEditorSync, selectEditorTab, syncBodyScrollLock, refreshHeaderButtons }; }); })();