Reestruturação dos arquivos e pastas do código da extensão

This commit is contained in:
Paulo Porto
2026-05-13 23:40:29 -03:00
parent f196067b21
commit c989088ab1
33 changed files with 13154 additions and 11866 deletions

328
src/content/shell-view.js Normal file
View File

@@ -0,0 +1,328 @@
(() => {
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,
});
})();