252 lines
19 KiB
JavaScript
252 lines
19 KiB
JavaScript
const UI_ICON_NS = "http://www.w3.org/2000/svg";
|
|
const UI_ICON_SHAPES = Object.freeze({
|
|
activity: [["path", { d: "M3 12h4l2-6 4 12 2-6h6" }]],
|
|
alert: [["path", { d: "M12 3 2.8 20h18.4L12 3Z" }], ["path", { d: "M12 9v4" }], ["circle", { cx: "12", cy: "17", r: ".7", fill: "currentColor", stroke: "none" }]],
|
|
archive: [["rect", { x: "3", y: "5", width: "18", height: "4", rx: "1" }], ["path", { d: "M5 9v10h14V9M9 13h6" }]],
|
|
book: [["path", { d: "M4 4.5h5.5A2.5 2.5 0 0 1 12 7v13a3 3 0 0 0-3-3H4V4.5ZM20 4.5h-5.5A2.5 2.5 0 0 0 12 7v13a3 3 0 0 1 3-3h5V4.5Z" }]],
|
|
box: [["path", { d: "m12 3 8 4.5v9L12 21l-8-4.5v-9L12 3Z" }], ["path", { d: "m4 7.5 8 4.5 8-4.5M12 12v9" }]],
|
|
certificate: [["rect", { x: "4", y: "3", width: "16", height: "14", rx: "2" }], ["path", { d: "M8 7h8M8 10h5M9 17l-1 4 4-2 4 2-1-4" }]],
|
|
check: [["path", { d: "m5 12 4 4L19 6" }]],
|
|
chevronDown: [["path", { d: "m6 9 6 6 6-6" }]],
|
|
chevronLeft: [["path", { d: "m15 6-6 6 6 6" }]],
|
|
chevronRight: [["path", { d: "m9 6 6 6-6 6" }]],
|
|
chevronUp: [["path", { d: "m6 15 6-6 6 6" }]],
|
|
code: [["path", { d: "m9 7-5 5 5 5M15 7l5 5-5 5M13 5l-2 14" }]],
|
|
command: [["rect", { x: "4", y: "4", width: "16", height: "16", rx: "4" }], ["path", { d: "M8 12h8M12 8v8" }]],
|
|
copy: [["rect", { x: "8", y: "8", width: "11", height: "11", rx: "2" }], ["path", { d: "M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2" }]],
|
|
cpu: [["rect", { x: "7", y: "7", width: "10", height: "10", rx: "2" }], ["path", { d: "M9 1v4M15 1v4M9 19v4M15 19v4M1 9h4M1 15h4M19 9h4M19 15h4M10 10h4v4h-4z" }]],
|
|
database: [["ellipse", { cx: "12", cy: "5", rx: "7", ry: "3" }], ["path", { d: "M5 5v7c0 1.7 3.1 3 7 3s7-1.3 7-3V5M5 12v7c0 1.7 3.1 3 7 3s7-1.3 7-3v-7" }]],
|
|
download: [["path", { d: "M12 3v12m0 0-4-4m4 4 4-4M4 19h16" }]],
|
|
edit: [["path", { d: "M4 20h4l11-11-4-4L4 16v4ZM13.5 6.5l4 4" }]],
|
|
external: [["path", { d: "M14 4h6v6M20 4l-9 9" }], ["path", { d: "M18 13v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6" }]],
|
|
eye: [["path", { d: "M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6S2.5 12 2.5 12Z" }], ["circle", { cx: "12", cy: "12", r: "2.5" }]],
|
|
eyeOff: [["path", { d: "M3 3l18 18M10.6 6.2A10 10 0 0 1 12 6c6 0 9.5 6 9.5 6a15 15 0 0 1-3 3.6M6.1 6.2C3.8 8 2.5 12 2.5 12s3.5 6 9.5 6a10 10 0 0 0 3.2-.5M9.9 9.9a3 3 0 0 0 4.2 4.2" }]],
|
|
file: [["path", { d: "M6 3h8l4 4v14H6V3Z" }], ["path", { d: "M14 3v5h5" }]],
|
|
fileX: [["path", { d: "M6 3h8l4 4v14H6V3ZM14 3v5h5M9 13l6 6M15 13l-6 6" }]],
|
|
filter: [["path", { d: "M3 5h18l-7 8v6l-4 2v-8L3 5Z" }]],
|
|
globe: [["circle", { cx: "12", cy: "12", r: "9" }], ["path", { d: "M3 12h18M12 3c2.5 2.7 3.5 5.7 3.5 9S14.5 18.3 12 21M12 3C9.5 5.7 8.5 8.7 8.5 12S9.5 18.3 12 21" }]],
|
|
grid: [["rect", { x: "4", y: "4", width: "6", height: "6", rx: "1" }], ["rect", { x: "14", y: "4", width: "6", height: "6", rx: "1" }], ["rect", { x: "4", y: "14", width: "6", height: "6", rx: "1" }], ["rect", { x: "14", y: "14", width: "6", height: "6", rx: "1" }]],
|
|
history: [["path", { d: "M4 8V4m0 0h4M4 4l3 3a8 8 0 1 1-2.2 7" }], ["path", { d: "M12 8v5l3 2" }]],
|
|
key: [["circle", { cx: "8", cy: "12", r: "4" }], ["path", { d: "M12 12h9M18 12v3M15 12v2" }]],
|
|
list: [["path", { d: "M9 6h11M9 12h11M9 18h11" }], ["circle", { cx: "4.5", cy: "6", r: ".8", fill: "currentColor", stroke: "none" }], ["circle", { cx: "4.5", cy: "12", r: ".8", fill: "currentColor", stroke: "none" }], ["circle", { cx: "4.5", cy: "18", r: ".8", fill: "currentColor", stroke: "none" }]],
|
|
loader: [["path", { d: "M20 12a8 8 0 1 1-2.3-5.7" }]],
|
|
lock: [["rect", { x: "5", y: "10", width: "14", height: "10", rx: "2" }], ["path", { d: "M8 10V7a4 4 0 0 1 8 0v3" }]],
|
|
login: [["path", { d: "M10 5H5v14h5M14 8l4 4-4 4M8 12h10" }]],
|
|
logout: [["path", { d: "M14 5h5v14h-5M10 8l-4 4 4 4M16 12H6" }]],
|
|
menu: [["path", { d: "M4 7h16M4 12h16M4 17h16" }]],
|
|
network: [["rect", { x: "9", y: "3", width: "6", height: "5", rx: "1" }], ["rect", { x: "3", y: "16", width: "6", height: "5", rx: "1" }], ["rect", { x: "15", y: "16", width: "6", height: "5", rx: "1" }], ["path", { d: "M12 8v4M6 16v-4h12v4" }]],
|
|
palette: [["path", { d: "M12 3a9 9 0 1 0 0 18h1.5a2 2 0 0 0 0-4H12a2 2 0 0 1 0-4h4a5 5 0 0 0 0-10h-4Z" }], ["circle", { cx: "7", cy: "10", r: "1", fill: "currentColor", stroke: "none" }], ["circle", { cx: "9", cy: "6.5", r: "1", fill: "currentColor", stroke: "none" }], ["circle", { cx: "14", cy: "6", r: "1", fill: "currentColor", stroke: "none" }]],
|
|
pause: [["path", { d: "M8 5v14M16 5v14" }]],
|
|
pin: [["path", { d: "m9 3 6 6-2 2 4 4-2 2-4-4-2 2-6-6 2-2 4 4 2-2-4-4 2-2ZM5 19l4-4" }]],
|
|
play: [["path", { d: "m8 5 11 7-11 7V5Z" }]],
|
|
plus: [["path", { d: "M12 5v14M5 12h14" }]],
|
|
refresh: [["path", { d: "M20 7v5h-5M4 17v-5h5" }], ["path", { d: "M18.5 9A7 7 0 0 0 6.2 6.2L4 8M5.5 15A7 7 0 0 0 17.8 17.8L20 16" }]],
|
|
route: [["circle", { cx: "6", cy: "6", r: "2" }], ["circle", { cx: "18", cy: "18", r: "2" }], ["path", { d: "M8 6h4a4 4 0 0 1 4 4v0a4 4 0 0 1-4 4H8a2 2 0 0 0-2 2v0" }]],
|
|
save: [["path", { d: "M5 4h12l2 2v14H5V4Z" }], ["path", { d: "M8 4v6h8V4M8 20v-6h8v6" }]],
|
|
search: [["circle", { cx: "10.5", cy: "10.5", r: "6.5" }], ["path", { d: "m15.5 15.5 5 5" }]],
|
|
settings: [["circle", { cx: "12", cy: "12", r: "3" }], ["path", { d: "M12 3v2M12 19v2M3 12h2M19 12h2M5.6 5.6 7 7M17 17l1.4 1.4M18.4 5.6 17 7M7 17l-1.4 1.4" }]],
|
|
shield: [["path", { d: "M12 3 20 6v6c0 5-3.3 8-8 9-4.7-1-8-4-8-9V6l8-3Z" }], ["path", { d: "m8.5 12 2.2 2.2 4.8-5" }]],
|
|
sliders: [["path", { d: "M4 7h6M14 7h6M4 17h10M18 17h2M10 4v6M14 14v6" }]],
|
|
sortDown: [["path", { d: "M8 5v14M5 16l3 3 3-3M14 8h5M14 12h4M14 16h3" }]],
|
|
sortUp: [["path", { d: "M8 19V5M5 8l3-3 3 3M14 8h3M14 12h4M14 16h5" }]],
|
|
trash: [["path", { d: "M4 7h16M9 3h6l1 4H8l1-4ZM7 7l1 14h8l1-14M10 11v6M14 11v6" }]],
|
|
undo: [["path", { d: "M9 7H4v-5M4 7l4-4M4.5 8A8 8 0 1 1 5 17" }]],
|
|
upload: [["path", { d: "M12 16V4m0 0-4 4m4-4 4 4M4 20h16" }]],
|
|
users: [["circle", { cx: "9", cy: "9", r: "3" }], ["path", { d: "M3 20v-2a5 5 0 0 1 10 0v2M16 8a3 3 0 0 1 0 6M16 15a5 5 0 0 1 5 5" }]],
|
|
wand: [["path", { d: "m4 20 12-12 3 3L7 23 4 20ZM14 4l1-3 1 3 3 1-3 1-1 3-1-3-3-1 3-1ZM19 14l.7-2 .8 2 2 .8-2 .7-.8 2-.7-2-2-.7 2-.8Z" }]],
|
|
x: [["path", { d: "M6 6l12 12M18 6 6 18" }]],
|
|
});
|
|
|
|
const UI_PANEL_ICONS = Object.freeze({
|
|
overview: "grid", vhosts: "network", "web-server": "globe", "docker-vhosts": "box",
|
|
redirects: "route", notfound: "fileX", streams: "network", "acl-profiles": "shield",
|
|
tls: "certificate", traffic: "activity", cache: "database", transform: "wand", settings: "settings",
|
|
});
|
|
const UI_SETTINGS_TAB_ICONS = Object.freeze({
|
|
appearance: "palette", listeners: "network", connections: "globe", "docker-agents": "box",
|
|
policies: "shield", cache: "database", transform: "wand", forwarding: "route", pages: "file",
|
|
logging: "list", geoip: "globe", prometheus: "activity", administration: "settings",
|
|
"system-info": "cpu", "certificate-api": "key", "tls-policy": "lock", "manual-config": "code", security: "shield",
|
|
});
|
|
const UI_VHOST_TAB_ICONS = Object.freeze({
|
|
details: "sliders", backends: "network", health: "activity", locations: "route", auth: "key", ssl: "lock",
|
|
cache: "database", compression: "archive", transform: "wand", security: "shield", pages: "file", advanced: "code",
|
|
});
|
|
const UI_TRANSLATED_ICON_KEYS = Object.freeze([
|
|
["trash", ["remove", "delete", "draftDiscard", "cacheRemoveBan", "certificateApiRevokeAll", "revokeAll"]],
|
|
["x", ["close", "cancelAction", "overviewLayoutInlineCancel"]],
|
|
["save", ["saveCertificate", "savePassword", "dashboardPresetsSave", "overviewLayoutInlineSave"]],
|
|
["check", ["apply", "applyFilters", "savePolicy", "continueAction", "webServicePresetApply", "dockerApplyService"]],
|
|
["plus", ["addCertificate", "vhNew", "webServiceNew", "redirectNew", "notfoundNew", "streamNew", "aclProfileNew", "phpProfileAdd", "redirectAddRule", "streamAddListener", "aclProfileAddIpException", "apiTokenCreate"]],
|
|
["refresh", ["refresh", "refreshNow", "reload", "reloadAudit", "reloadCertificates", "tunnelsReload", "dockerRefresh", "cacheRefresh", "sessionRetry"]],
|
|
["undo", ["configHistoryRestoreAction", "dashboardPresetsFactory", "draftRestore"]],
|
|
["edit", ["edit"]],
|
|
["download", ["export"]],
|
|
["upload", ["acmeImport", "aclProfileImportRules"]],
|
|
["eye", ["certificateReadNames", "dockerShowHidden", "dashboardWidgetsShowAll"]],
|
|
["settings", ["settings", "vhManage", "dockerAgentsSettings"]],
|
|
["book", ["webServiceHtaccessDocs", "webServicePhpDocsButton"]],
|
|
["pause", ["logPause"]],
|
|
["pin", ["logPin"]],
|
|
["history", ["configHistory"]],
|
|
["chevronLeft", ["previous"]],
|
|
["chevronRight", ["next"]],
|
|
]);
|
|
|
|
function uiIconSvg(name) {
|
|
const definition = UI_ICON_SHAPES[name] || UI_ICON_SHAPES.command;
|
|
const svg = document.createElementNS(UI_ICON_NS, "svg");
|
|
svg.classList.add("ui-icon"); svg.dataset.uiIcon = name; svg.setAttribute("viewBox", "0 0 24 24");
|
|
svg.setAttribute("aria-hidden", "true"); svg.setAttribute("focusable", "false");
|
|
svg.setAttribute("fill", "none"); svg.setAttribute("stroke", "currentColor"); svg.setAttribute("stroke-width", "1.8");
|
|
svg.setAttribute("stroke-linecap", "round"); svg.setAttribute("stroke-linejoin", "round");
|
|
for (const [tag, attrs] of definition) {
|
|
const node = document.createElementNS(UI_ICON_NS, tag);
|
|
for (const [key, value] of Object.entries(attrs)) node.setAttribute(key, value);
|
|
svg.append(node);
|
|
}
|
|
return svg;
|
|
}
|
|
function uiIconStrings(target) {
|
|
return [target.textContent, target.getAttribute("aria-label"), target.title].filter(Boolean).map(value => String(value).trim().toLocaleLowerCase());
|
|
}
|
|
function uiIconMatchesTranslation(target, keys) {
|
|
if (typeof t !== "function") return false;
|
|
const strings = uiIconStrings(target); if (!strings.length) return false;
|
|
for (const key of keys) {
|
|
const translated = String(t(key) || "").trim().toLocaleLowerCase();
|
|
if (!translated || translated === key.toLocaleLowerCase()) continue;
|
|
if (strings.some(value => value === translated || value.startsWith(`${translated} `) || value.startsWith(`${translated}:`))) return true;
|
|
}
|
|
return false;
|
|
}
|
|
function uiIconForTarget(target) {
|
|
const manual = target.dataset.uiIconName;
|
|
if (manual === "none") return null;
|
|
if (manual && UI_ICON_SHAPES[manual]) return manual;
|
|
if (target.matches(".chart-style-choice")) return null;
|
|
if (target.matches(".overview-inline-grip, .overview-inline-narrow, .overview-inline-widen, .overview-inline-up, .overview-inline-down, .overview-inline-hide, .overview-layout-narrow, .overview-layout-widen, .overview-layout-up, .overview-layout-down, .overview-layout-hide")) return null;
|
|
if (target.matches("#tunnels-open")) return null;
|
|
if (target.matches("#web-service-preset-apply")) return "wand";
|
|
if (target.matches(".listener-h2-settings-link, .listener-h3-settings-link")) return "settings";
|
|
if (target.dataset.panel && UI_PANEL_ICONS[target.dataset.panel]) return UI_PANEL_ICONS[target.dataset.panel];
|
|
if (target.dataset.settingsTab && UI_SETTINGS_TAB_ICONS[target.dataset.settingsTab]) return UI_SETTINGS_TAB_ICONS[target.dataset.settingsTab];
|
|
if (target.dataset.tab && UI_VHOST_TAB_ICONS[target.dataset.tab]) return UI_VHOST_TAB_ICONS[target.dataset.tab];
|
|
if (target.dataset.certificateTab) return ({ installed: "certificate", renewal: "refresh", generate: "plus" })[target.dataset.certificateTab] || "certificate";
|
|
if (target.dataset.trafficTab) return ({ overview: "grid", clients: "users", requests: "list", delivery: "activity" })[target.dataset.trafficTab] || "activity";
|
|
if (target.dataset.httpSettingsTab) return "network";
|
|
if (target.dataset.policySettingsTab) return target.dataset.policySettingsTab === "headers" || target.dataset.policySettingsTab === "response" ? "list" : "shield";
|
|
if (target.dataset.cacheSettingsTab) return ({ general: "database", resilience: "shield", cold: "archive", diagnostics: "activity" })[target.dataset.cacheSettingsTab] || "database";
|
|
if (target.dataset.loggingSettingsTab) return ({ outputs: "list", format: "code", errors: "alert" })[target.dataset.loggingSettingsTab] || "list";
|
|
if (target.dataset.dashboardWidgetAction === "toggle") return target.classList.contains("is-hidden") ? "eyeOff" : "eye";
|
|
if (target.dataset.dashboardWidgetAction === "up") return "chevronLeft";
|
|
if (target.dataset.dashboardWidgetAction === "down") return "chevronRight";
|
|
|
|
const signature = [target.id, target.dataset.i18n, target.dataset.i18nAria, target.dataset.action, target.className]
|
|
.filter(Boolean).join(" ").toLocaleLowerCase();
|
|
const aria = String(target.getAttribute("aria-label") || "").toLocaleLowerCase();
|
|
if (/mobile-menu|menu-toggle/.test(signature)) return "menu";
|
|
if (/is-saving|saving/.test(signature)) return "loader";
|
|
if (/sort-direction|sort-button/.test(signature)) return /descending|desc/.test(aria) ? "sortDown" : "sortUp";
|
|
if (/scroll-prev|move-earlier|layout-up/.test(signature)) return "chevronLeft";
|
|
if (/scroll-next|move-later|layout-down/.test(signature)) return "chevronRight";
|
|
if (/collapse|narrow/.test(signature)) return "chevronDown";
|
|
if (/expand|widen/.test(signature)) return "chevronUp";
|
|
if (/health/.test(signature) && !/certificate/.test(signature)) return "activity";
|
|
if (/copy/.test(signature)) return "copy";
|
|
if (/export|download/.test(signature)) return "download";
|
|
if (/import|upload/.test(signature)) return "upload";
|
|
if (/search|find/.test(signature)) return "search";
|
|
if (/filter/.test(signature)) return "filter";
|
|
if (/inspect|preview|read-names|readnames|show-hidden|visibility|view-toggle/.test(signature)) return "eye";
|
|
if (/history|revision/.test(signature)) return "history";
|
|
if (/restore|factory/.test(signature)) return "undo";
|
|
if (/refresh|reload|retry|renew|update-now|updatenow/.test(signature)) return "refresh";
|
|
if (/reset/.test(signature)) return "undo";
|
|
if (/revoke|remove|delete|discard|purge|kill/.test(signature)) return "trash";
|
|
if (/close|cancel/.test(signature)) return "x";
|
|
if (/new|add|create|generate|issue/.test(signature)) return "plus";
|
|
if (/save/.test(signature)) return "save";
|
|
if (/apply|submit|continue/.test(signature)) return "check";
|
|
if (/edit/.test(signature)) return "edit";
|
|
if (/pause/.test(signature)) return "pause";
|
|
if (/pin|dock/.test(signature)) return "pin";
|
|
if (/start|run-operation|cleanup-start/.test(signature)) return "play";
|
|
if (/logout|sign-out|lock/.test(signature)) return "logout";
|
|
if (/login|sign-in/.test(signature)) return "login";
|
|
if (/certificate-api|api-token|token|secret|key/.test(signature)) return "key";
|
|
if (/certificate|acme|tls/.test(signature)) return "certificate";
|
|
if (/security|acl|policy|audit/.test(signature)) return "shield";
|
|
if (/settings|manage|configuration/.test(signature)) return "settings";
|
|
if (/docker/.test(signature)) return "box";
|
|
if (/vhost|backend|stream|tunnel|listener/.test(signature)) return "network";
|
|
if (/redirect|forward/.test(signature)) return "route";
|
|
if (/notfound|404/.test(signature)) return "fileX";
|
|
if (/cache|cold/.test(signature)) return "database";
|
|
if (/transform|minif/.test(signature)) return "wand";
|
|
if (/traffic|metric|prometheus/.test(signature)) return "activity";
|
|
if (/log/.test(signature)) return "list";
|
|
if (/docs|help|documentation/.test(signature)) return "book";
|
|
if (/php|json|advanced|code/.test(signature)) return "code";
|
|
if (/layout|preset|dashboard|overview/.test(signature)) return "grid";
|
|
if (/about|info/.test(signature)) return "book";
|
|
if (target.matches("a[href]")) return target.href?.startsWith(location.origin) ? "file" : "external";
|
|
for (const [icon, keys] of UI_TRANSLATED_ICON_KEYS) if (uiIconMatchesTranslation(target, keys)) return icon;
|
|
if (aria && typeof t === "function") {
|
|
if (aria.startsWith(String(t("remove")).toLocaleLowerCase())) return "trash";
|
|
if (aria.startsWith(String(t("copyValue")).toLocaleLowerCase())) return "copy";
|
|
if (aria === String(t("dashboardWidgetsMoveEarlier")).toLocaleLowerCase()) return "chevronLeft";
|
|
if (aria === String(t("dashboardWidgetsMoveLater")).toLocaleLowerCase()) return "chevronRight";
|
|
}
|
|
return "command";
|
|
}
|
|
const UI_ICON_TARGET_SELECTOR = "button, a.primary, a.outline, a.quiet, #logs-menu > summary";
|
|
function uiIconTargetIsEligible(target) {
|
|
return target instanceof Element && target.matches(UI_ICON_TARGET_SELECTOR);
|
|
}
|
|
function uiDecorateIconTarget(target) {
|
|
if (!uiIconTargetIsEligible(target)) return;
|
|
const existing = target.querySelector(":scope > svg.ui-icon");
|
|
if (document.documentElement.dataset.interfaceIcons !== "true") { existing?.remove(); return; }
|
|
const name = uiIconForTarget(target);
|
|
if (!name) { existing?.remove(); return; }
|
|
if (existing?.dataset.uiIcon === name) return;
|
|
existing?.remove(); target.prepend(uiIconSvg(name));
|
|
}
|
|
function refreshUiIcons(root = document) {
|
|
if (root instanceof Element && uiIconTargetIsEligible(root)) uiDecorateIconTarget(root);
|
|
root.querySelectorAll?.(UI_ICON_TARGET_SELECTOR).forEach(uiDecorateIconTarget);
|
|
}
|
|
let uiIconObserverActive = false;
|
|
function setUiIconsEnabled(value) {
|
|
const enabled = value === true;
|
|
document.documentElement.dataset.interfaceIcons = String(enabled);
|
|
if (enabled) {
|
|
if (!uiIconObserverActive) {
|
|
uiIconObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ["class", "aria-label", "aria-expanded", "aria-pressed", "data-i18n", "data-action"] });
|
|
uiIconObserverActive = true;
|
|
}
|
|
refreshUiIcons();
|
|
} else {
|
|
if (uiIconObserverActive) { uiIconObserver.disconnect(); uiIconObserverActive = false; }
|
|
document.querySelectorAll("svg.ui-icon").forEach(node => node.remove());
|
|
}
|
|
const input = $("ui-interface-icons"); if (input) input.checked = enabled;
|
|
return enabled;
|
|
}
|
|
const uiIconObserver = new MutationObserver(records => {
|
|
if (document.documentElement.dataset.interfaceIcons !== "true") return;
|
|
const targets = new Set();
|
|
for (const record of records) {
|
|
const element = record.target instanceof Element ? record.target : record.target.parentElement;
|
|
const action = element?.closest?.(UI_ICON_TARGET_SELECTOR); if (action) targets.add(action);
|
|
for (const node of record.addedNodes) {
|
|
if (!(node instanceof Element)) continue;
|
|
if (uiIconTargetIsEligible(node)) targets.add(node);
|
|
node.querySelectorAll?.(UI_ICON_TARGET_SELECTOR).forEach(item => targets.add(item));
|
|
}
|
|
}
|
|
targets.forEach(uiDecorateIconTarget);
|
|
});
|
|
window.rustProxyIcons = { refresh: refreshUiIcons, setEnabled: setUiIconsEnabled, iconFor: uiIconForTarget };
|