(() => { function create({ IDS, CLASSES, USER_BUTTON_LABEL, normalizeText, isVisible, }) { function applyDialogTheme(overlay, theme) { const dialog = overlay?.querySelector(`#${IDS.dialog}`); if (!dialog) { return; } dialog.setAttribute("data-theme", theme); } function ensureStyles() { // Styles are loaded by manifest.json. This function remains as a stable callsite. } function applyTheme(header, functionsContainer, anchor, mount, overlay) { const headerStyle = window.getComputedStyle(header); const functionsStyle = window.getComputedStyle(functionsContainer); const sourceBackground = resolveBackgroundColor(header, headerStyle); const sourceForeground = resolveForegroundColor( header, functionsContainer, anchor, headerStyle, functionsStyle ); const workbenchHeader = mix( sourceBackground, { r: 75, g: 51, b: 93, a: 1 }, 0.52 ); const workbenchAccent = mix( workbenchHeader, { r: 108, g: 77, b: 128, a: 1 }, 0.38 ); const focusColor = mix(sourceForeground, workbenchHeader, 0.28); const themeTargets = [mount, overlay].filter(Boolean); for (const target of themeTargets) { target.style.setProperty( "--arch-panel-bg", toColor(mix(sourceBackground, sourceForeground, 0.12)) ); target.style.setProperty( "--arch-panel-bg-hover", toColor(mix(sourceBackground, sourceForeground, 0.22)) ); target.style.setProperty( "--arch-panel-border", toColor(mix(sourceBackground, sourceForeground, 0.32)) ); target.style.setProperty("--arch-panel-fg", toColor(sourceForeground, 1)); target.style.setProperty("--arch-panel-focus", toColor(focusColor, 0.94)); target.style.setProperty( "--arch-panel-font", functionsStyle.fontFamily || headerStyle.fontFamily || "inherit" ); target.style.setProperty( "--arch-workbench-header", toColor(workbenchHeader, 1) ); target.style.setProperty( "--arch-workbench-accent", toColor(workbenchAccent, 1) ); } } function resolveForegroundColor( header, functionsContainer, anchor, headerStyle, functionsStyle ) { const salesPlanningElement = findTextElement(header, "Sales Planning"); const salesPlanningColor = salesPlanningElement ? parseColor(window.getComputedStyle(salesPlanningElement).color) : null; const functionsItemColor = findVisibleChildColor(functionsContainer); const userColor = anchor ? parseColor(window.getComputedStyle(anchor).color) : null; return ( userColor || functionsItemColor || parseColor(functionsStyle.color) || salesPlanningColor || parseColor(headerStyle.color) || { r: 255, g: 255, b: 255, a: 1, } ); } function resolveBackgroundColor(element, style) { const directBackground = parseColor(style.backgroundColor); if (directBackground && directBackground.a > 0) { return directBackground; } let current = element.parentElement; while (current) { const currentBackground = parseColor( window.getComputedStyle(current).backgroundColor ); if (currentBackground && currentBackground.a > 0) { return currentBackground; } current = current.parentElement; } return { r: 75, g: 51, b: 93, a: 1 }; } function parseColor(value) { if (!value || value === "transparent") { return null; } const rgbMatch = value.match( /rgba?\(\s*(\d{1,3})[\s,]+(\d{1,3})[\s,]+(\d{1,3})(?:[\s,/]+([.\d]+))?\s*\)/i ); if (rgbMatch) { return { r: Number.parseInt(rgbMatch[1], 10), g: Number.parseInt(rgbMatch[2], 10), b: Number.parseInt(rgbMatch[3], 10), a: rgbMatch[4] === undefined ? 1 : Number.parseFloat(rgbMatch[4]), }; } const hexMatch = value.match(/^#([\da-f]{3,8})$/i); if (!hexMatch) { return null; } const hexValue = hexMatch[1]; if (hexValue.length === 3 || hexValue.length === 4) { const [r, g, b, a = "f"] = hexValue.split(""); return { r: Number.parseInt(r + r, 16), g: Number.parseInt(g + g, 16), b: Number.parseInt(b + b, 16), a: Number.parseInt(a + a, 16) / 255, }; } if (hexValue.length === 6 || hexValue.length === 8) { return { r: Number.parseInt(hexValue.slice(0, 2), 16), g: Number.parseInt(hexValue.slice(2, 4), 16), b: Number.parseInt(hexValue.slice(4, 6), 16), a: hexValue.length === 8 ? Number.parseInt(hexValue.slice(6, 8), 16) / 255 : 1, }; } return null; } function mix(base, tint, amount) { const ratio = clamp(amount, 0, 1); return { r: Math.round(base.r + (tint.r - base.r) * ratio), g: Math.round(base.g + (tint.g - base.g) * ratio), b: Math.round(base.b + (tint.b - base.b) * ratio), a: base.a + (tint.a - base.a) * ratio, }; } function toColor(color, alphaOverride) { const alpha = alphaOverride ?? color.a ?? 1; return `rgba(${color.r}, ${color.g}, ${color.b}, ${clamp(alpha, 0, 1)})`; } function clamp(value, min, max) { return Math.min(Math.max(value, min), max); } function findUserAnchor(container) { const selector = [ `button[label="${USER_BUTTON_LABEL}"]`, `[role="button"][label="${USER_BUTTON_LABEL}"]`, `oj-button[label="${USER_BUTTON_LABEL}"]`, `button[aria-label="${USER_BUTTON_LABEL}"]`, `[role="button"][aria-label="${USER_BUTTON_LABEL}"]`, `[label="${USER_BUTTON_LABEL}"]`, `[aria-label="${USER_BUTTON_LABEL}"]`, ].join(", "); const directMatch = container.querySelector(selector); if (directMatch && isVisible(directMatch)) { return directMatch; } const fallbackMatch = Array.from(container.querySelectorAll("*")).find( (element) => { if (!isVisible(element) || element.closest(`#${IDS.mount}`)) { return false; } return ( element.getAttribute("label") === USER_BUTTON_LABEL || element.getAttribute("aria-label") === USER_BUTTON_LABEL ); } ); return fallbackMatch || null; } function findTextElement(container, expectedText) { const normalizedExpected = normalizeText(expectedText); const elements = Array.from(container.querySelectorAll("*")); return ( elements.find((element) => { const text = normalizeText(element.textContent || ""); return text && text.includes(normalizedExpected); }) || null ); } function resolveInsertionTarget(container, anchor, mount) { const directAnchor = anchor ? getDirectChild(anchor, container) : null; const fallbackTarget = findFirstInsertionTarget(container, mount); const candidate = directAnchor || fallbackTarget; if (!candidate) { return null; } if (candidate === mount) { return findNextValidSibling(container, mount); } return candidate.parentElement === container ? candidate : null; } function getDirectChild(element, container) { let current = element; while (current && current.parentElement !== container) { current = current.parentElement; } return current && current.parentElement === container ? current : null; } function findFirstInsertionTarget(container, mount) { return Array.from(container.children).find((child) => child !== mount) || null; } function findNextValidSibling(container, mount) { let sibling = mount.nextElementSibling; while (sibling) { if (sibling.parentElement === container) { return sibling; } sibling = sibling.nextElementSibling; } return null; } function findVisibleChildColor(container) { const visibleChild = Array.from(container.querySelectorAll("*")).find( (element) => isVisible(element) && !element.closest(`#${IDS.mount}`) ); return visibleChild ? parseColor(window.getComputedStyle(visibleChild).color) : null; } function lockScroll() { document.documentElement.classList.add(CLASSES.modalOpen); document.body?.classList.add(CLASSES.modalOpen); } function unlockScroll() { document.documentElement.classList.remove(CLASSES.modalOpen); document.body?.classList.remove(CLASSES.modalOpen); } return Object.freeze({ applyDialogTheme, ensureStyles, applyTheme, findUserAnchor, resolveInsertionTarget, lockScroll, unlockScroll, }); } globalThis.ArchPanelShellView = Object.freeze({ create, }); })();