import MenuitemSVGKey from "@/seqta/content/MenuItemSVGKey.json"; import { ChangeMenuItemPositions, MenuOptionsOpen, } from "@/seqta/utils/Openers/OpenMenuOptions"; import { settingsState } from "@/seqta/utils/listeners/SettingsState"; import stringToHTML from "@/seqta/utils/stringToHTML"; import { waitForEngageMenuList } from "@/seqta/utils/waitForEngageMenuList"; import { waitForElm } from "@/seqta/utils/waitForElm"; import { eventManager } from "@/seqta/utils/listeners/EventManager"; import { isSeqtaEngageExperience } from "@/seqta/utils/isSeqtaEngage"; const BETTERSEQTA_ICON_ATTR = "data-betterseqta-icon"; function getMenuLabel(element: HTMLElement): HTMLElement | null { const label = element.querySelector(":scope > label"); return label instanceof HTMLElement ? label : null; } function getTopLevelMenuList(menu = document.getElementById("menu")): HTMLElement | null { if (!menu) return null; return ( (menu.querySelector(":scope > ul") as HTMLElement | null) ?? (menu.querySelector("ul") as HTMLElement | null) ); } export function isTopLevelSidebarItem(node: HTMLElement): boolean { if (!node.classList.contains("item")) return false; if (node.nodeName !== "LI" && node.nodeName !== "SECTION") return false; const topList = getTopLevelMenuList(); return !!topList && node.parentElement === topList; } function wrapMenuLabelText(label: HTMLElement) { const textNode = label.lastChild; if ( textNode?.nodeType === 3 && textNode.parentNode && textNode.parentNode.nodeName !== "SPAN" ) { const span = document.createElement("span"); span.textContent = textNode.nodeValue; label.replaceChild(span, textNode); } } export function replaceMenuSVG(element: HTMLElement, svg: string) { const label = getMenuLabel(element); if (!label?.firstChild) return; if (label.firstElementChild?.getAttribute(BETTERSEQTA_ICON_ATTR) === "true") { return; } label.firstChild.remove(); label.innerHTML = `${label.innerHTML}`; const newSvg = stringToHTML(svg).firstChild; if (!(newSvg instanceof Element)) return; newSvg.setAttribute(BETTERSEQTA_ICON_ATTR, "true"); label.insertBefore(newSvg, label.firstChild); } export function processMenuItemNode(node: HTMLElement) { if (!isTopLevelSidebarItem(node) || MenuOptionsOpen) return; const key = node.dataset.key as keyof typeof MenuitemSVGKey | undefined; if (key && MenuitemSVGKey[key]) { replaceMenuSVG(node, MenuitemSVGKey[key]); } else { const label = getMenuLabel(node); if (label) wrapMenuLabelText(label); } } function processTopLevelMenuItems(reorder = !isSeqtaEngageExperience()) { if (MenuOptionsOpen) return; const topList = getTopLevelMenuList(); if (!topList) return; for (const child of topList.children) { if (child instanceof HTMLElement) { processMenuItemNode(child); } } if (reorder) { ChangeMenuItemPositions(settingsState.menuorder); } } let engageMenuIconObserver: MutationObserver | null = null; let engageMenuIconFrame: number | null = null; function scheduleEngageMenuIconPass() { if (engageMenuIconFrame !== null) return; engageMenuIconFrame = window.requestAnimationFrame(() => { engageMenuIconFrame = null; processTopLevelMenuItems(false); }); } async function observeEngageMenuIcons() { const menuList = await waitForEngageMenuList(); const menu = document.getElementById("menu"); if (!menu || !menuList) return; processTopLevelMenuItems(false); engageMenuIconObserver?.disconnect(); engageMenuIconObserver = new MutationObserver(() => { scheduleEngageMenuIconPass(); }); engageMenuIconObserver.observe(menu, { childList: true, subtree: true, }); } const processedSymbol = Symbol("processed"); export async function observeMenuItemPosition() { if (isSeqtaEngageExperience()) { await observeEngageMenuIcons(); return; } await waitForElm("#menu > ul > li"); eventManager.register( "menuList", { parentElement: document.querySelector("#menu")!.firstChild as Element, }, (element: Element) => { const node = element as HTMLElement; if (!isTopLevelSidebarItem(node)) return; if ((element as any)[processedSymbol]) return; if (!MenuOptionsOpen) { processMenuItemNode(node); ChangeMenuItemPositions(settingsState.menuorder); (element as any)[processedSymbol] = true; } }, ); }