All checks were successful
Build and Release Sanctum / Build App (push) Successful in 1m55s
397 lines
12 KiB
JavaScript
397 lines
12 KiB
JavaScript
(function () {
|
|
if (window.__sanctumGamePresenceSettings) return;
|
|
window.__sanctumGamePresenceSettings = true;
|
|
|
|
const CLONE_ATTR = "data-sanctum-game-presence";
|
|
const PANEL_ATTR = "data-sanctum-game-presence-panel";
|
|
const POPULAR_GAMES = [
|
|
"Apex Legends",
|
|
"Among Us",
|
|
"Assassin's Creed Mirage",
|
|
"Assassin's Creed Valhalla",
|
|
"Armored Core VI: Fires of Rubicon",
|
|
"Baldur's Gate 3",
|
|
"Black Myth: Wukong",
|
|
"Brawlhalla",
|
|
"Call of Duty: Black Ops 6",
|
|
"Call of Duty: Modern Warfare III",
|
|
"Call of Duty: Warzone",
|
|
"Celeste",
|
|
"Cities: Skylines II",
|
|
"Civilization VI",
|
|
"Counter-Strike 2",
|
|
"Cuphead",
|
|
"Cyberpunk 2077",
|
|
"Dark Souls III",
|
|
"Dave the Diver",
|
|
"Days Gone",
|
|
"Dead by Daylight",
|
|
"Dead Cells",
|
|
"Deep Rock Galactic",
|
|
"Destiny 2",
|
|
"Diablo IV",
|
|
"Dota 2",
|
|
"Dragon's Dogma 2",
|
|
"Elden Ring",
|
|
"Enshrouded",
|
|
"Escape from Tarkov",
|
|
"Euro Truck Simulator 2",
|
|
"EVE Online",
|
|
"Fall Guys",
|
|
"Fallout 4",
|
|
"Fallout 76",
|
|
"Factorio",
|
|
"F1 24",
|
|
"Final Fantasy XIV",
|
|
"Forza Horizon 5",
|
|
"Fortnite",
|
|
"Genshin Impact",
|
|
"Ghost of Tsushima",
|
|
"God of War",
|
|
"Grand Theft Auto V",
|
|
"Grounded",
|
|
"Guild Wars 2",
|
|
"Hades",
|
|
"Hades II",
|
|
"Helldivers 2",
|
|
"Hogwarts Legacy",
|
|
"Hollow Knight",
|
|
"Honkai: Star Rail",
|
|
"Honkai Impact 3rd",
|
|
"Hunt: Showdown",
|
|
"It Takes Two",
|
|
"Kingdom Come: Deliverance",
|
|
"League of Legends",
|
|
"Lethal Company",
|
|
"Left 4 Dead 2",
|
|
"Last Epoch",
|
|
"Marvel Rivals",
|
|
"Minecraft",
|
|
"Monster Hunter: World",
|
|
"Monster Hunter Rise",
|
|
"Mortal Kombat 1",
|
|
"Metaphor: ReFantazio",
|
|
"No Man's Sky",
|
|
"Once Human",
|
|
"Overwatch 2",
|
|
"Palworld",
|
|
"Path of Exile",
|
|
"Path of Exile 2",
|
|
"Persona 5 Royal",
|
|
"Phasmophobia",
|
|
"PUBG: Battlegrounds",
|
|
"Paladins",
|
|
"Rainbow Six Siege",
|
|
"Red Dead Redemption 2",
|
|
"Resident Evil 4",
|
|
"Resident Evil Village",
|
|
"Rocket League",
|
|
"Rust",
|
|
"Satisfactory",
|
|
"Sea of Thieves",
|
|
"Skyrim Special Edition",
|
|
"Slay the Spire",
|
|
"Sons of the Forest",
|
|
"Spider-Man Remastered",
|
|
"Split Fiction",
|
|
"Star Citizen",
|
|
"Starfield",
|
|
"Stardew Valley",
|
|
"Street Fighter 6",
|
|
"Subnautica",
|
|
"Team Fortress 2",
|
|
"Tekken 8",
|
|
"Terraria",
|
|
"The Elder Scrolls Online",
|
|
"The Finals",
|
|
"The Last of Us Part I",
|
|
"The Witcher 3",
|
|
"Titanfall 2",
|
|
"VALORANT",
|
|
"V Rising",
|
|
"Valheim",
|
|
"Warframe",
|
|
"War Thunder",
|
|
"Wuthering Waves",
|
|
"World of Warcraft",
|
|
"World of Tanks",
|
|
"World of Warships",
|
|
"Zenless Zone Zero",
|
|
];
|
|
|
|
function toggleCheckbox(elem, value) {
|
|
const checkbox = elem.querySelector("mdui-checkbox");
|
|
if (!checkbox) return;
|
|
|
|
if (value) {
|
|
checkbox.setAttribute("checked", "");
|
|
checkbox.setAttribute("value", "on");
|
|
} else {
|
|
checkbox.removeAttribute("checked");
|
|
checkbox.setAttribute("value", "off");
|
|
}
|
|
}
|
|
|
|
function getConfig() {
|
|
return window.desktopConfig.get();
|
|
}
|
|
|
|
function setConfig(next) {
|
|
window.desktopConfig.set(next);
|
|
}
|
|
|
|
function buildPanel() {
|
|
const panel = document.createElement("div");
|
|
panel.setAttribute(PANEL_ATTR, "true");
|
|
panel.style.cssText = `
|
|
display: none;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
margin-top: 8px;
|
|
padding: 10px 12px;
|
|
border-radius: 10px;
|
|
background: rgba(255,255,255,0.04);
|
|
border: 1px solid rgba(255,255,255,0.08);
|
|
color: inherit;
|
|
`;
|
|
|
|
const note = document.createElement("div");
|
|
note.style.cssText = "font-size:12px; opacity:0.75; line-height:1.35;";
|
|
note.textContent =
|
|
"Sanctum only lights up for games in its built-in catalog or names you add below.";
|
|
panel.appendChild(note);
|
|
|
|
const allowLabel = document.createElement("div");
|
|
allowLabel.textContent = "Allowed games / windows";
|
|
allowLabel.style.cssText = "font-size:12px; font-weight:600;";
|
|
panel.appendChild(allowLabel);
|
|
|
|
const textarea = document.createElement("textarea");
|
|
textarea.value = getConfig().gamePresenceAllowList || "";
|
|
textarea.rows = 4;
|
|
textarea.placeholder = "Examples: Fortnite, Valorant, Counter-Strike 2, Baldur's Gate 3";
|
|
textarea.style.cssText = `
|
|
width: 100%;
|
|
resize: vertical;
|
|
min-height: 88px;
|
|
padding: 8px 10px;
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(255,255,255,0.12);
|
|
background: rgba(0,0,0,0.18);
|
|
color: inherit;
|
|
font: inherit;
|
|
line-height: 1.4;
|
|
`;
|
|
textarea.addEventListener("input", () => {
|
|
const config = getConfig();
|
|
config.gamePresenceAllowList = textarea.value;
|
|
setConfig(config);
|
|
});
|
|
panel.appendChild(textarea);
|
|
|
|
const pickerLabel = document.createElement("div");
|
|
pickerLabel.textContent = "Popular games";
|
|
pickerLabel.style.cssText = "font-size:12px; font-weight:600; margin-top:2px;";
|
|
panel.appendChild(pickerLabel);
|
|
|
|
const pickerHint = document.createElement("div");
|
|
pickerHint.textContent = "Search and add games from the built-in catalog.";
|
|
pickerHint.style.cssText = "font-size:11px; opacity:0.7; line-height:1.35;";
|
|
panel.appendChild(pickerHint);
|
|
|
|
const pickerRow = document.createElement("div");
|
|
pickerRow.style.cssText = "display:flex; gap:8px; align-items:center;";
|
|
|
|
const pickerSearch = document.createElement("input");
|
|
pickerSearch.type = "search";
|
|
pickerSearch.placeholder = "Search popular games";
|
|
pickerSearch.style.cssText = `
|
|
flex: 1;
|
|
min-width: 0;
|
|
padding: 8px 10px;
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(255,255,255,0.12);
|
|
background: rgba(0,0,0,0.18);
|
|
color: inherit;
|
|
font: inherit;
|
|
`;
|
|
|
|
const pickerAdd = document.createElement("button");
|
|
pickerAdd.type = "button";
|
|
pickerAdd.textContent = "Add";
|
|
pickerAdd.style.cssText = `
|
|
flex-shrink: 0;
|
|
padding: 8px 12px;
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(255,255,255,0.12);
|
|
background: rgba(255,255,255,0.08);
|
|
color: inherit;
|
|
font: inherit;
|
|
cursor: pointer;
|
|
`;
|
|
|
|
pickerRow.appendChild(pickerSearch);
|
|
pickerRow.appendChild(pickerAdd);
|
|
panel.appendChild(pickerRow);
|
|
|
|
const pickerList = document.createElement("div");
|
|
pickerList.style.cssText = `
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
|
|
gap: 6px;
|
|
max-height: 180px;
|
|
overflow: auto;
|
|
padding-right: 2px;
|
|
`;
|
|
panel.appendChild(pickerList);
|
|
|
|
function existingEntries() {
|
|
return textarea.value
|
|
.split(/[\n,]+/)
|
|
.map((item) => item.trim())
|
|
.filter(Boolean);
|
|
}
|
|
|
|
function addGameToAllowList(name) {
|
|
const current = new Set(existingEntries().map((item) => item.toLowerCase()));
|
|
if (current.has(name.toLowerCase())) return;
|
|
const next = existingEntries();
|
|
next.push(name);
|
|
textarea.value = next.join("\n");
|
|
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
}
|
|
|
|
function renderPicker() {
|
|
const query = pickerSearch.value.trim().toLowerCase();
|
|
const selected = new Set(existingEntries().map((item) => item.toLowerCase()));
|
|
pickerList.innerHTML = "";
|
|
|
|
const matches = POPULAR_GAMES.filter((game) => !query || game.toLowerCase().includes(query)).slice(0, 40);
|
|
for (const game of matches) {
|
|
const button = document.createElement("button");
|
|
button.type = "button";
|
|
button.textContent = selected.has(game.toLowerCase()) ? `✓ ${game}` : game;
|
|
button.title = selected.has(game.toLowerCase()) ? "Already added" : `Add ${game}`;
|
|
button.style.cssText = `
|
|
padding: 8px 10px;
|
|
border-radius: 10px;
|
|
border: 1px solid ${selected.has(game.toLowerCase()) ? "rgba(104, 126, 255, 0.55)" : "rgba(255,255,255,0.12)"};
|
|
background: ${selected.has(game.toLowerCase()) ? "rgba(104, 126, 255, 0.16)" : "rgba(255,255,255,0.05)"};
|
|
color: inherit;
|
|
font: inherit;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
`;
|
|
button.addEventListener("click", () => addGameToAllowList(game));
|
|
pickerList.appendChild(button);
|
|
}
|
|
}
|
|
|
|
pickerSearch.addEventListener("input", renderPicker);
|
|
pickerAdd.addEventListener("click", () => {
|
|
const query = pickerSearch.value.trim();
|
|
if (!query) return;
|
|
const exact = POPULAR_GAMES.find((game) => game.toLowerCase() === query.toLowerCase());
|
|
if (exact) {
|
|
addGameToAllowList(exact);
|
|
return;
|
|
}
|
|
addGameToAllowList(query);
|
|
});
|
|
textarea.addEventListener("input", renderPicker);
|
|
renderPicker();
|
|
|
|
return panel;
|
|
}
|
|
|
|
function createButton(baseElem) {
|
|
const row = baseElem.cloneNode(true);
|
|
row.setAttribute(CLONE_ATTR, "true");
|
|
|
|
const title = row.querySelector("div.d_flex.flex-g_1 > div");
|
|
const desc = row.querySelector("div.d_flex.flex-g_1 > span");
|
|
const icon = row.querySelector("div.w_36px span.material-symbols-outlined");
|
|
const existingIcon = row.querySelector("div.w_36px");
|
|
|
|
if (title) title.textContent = "Gameplay overlay";
|
|
if (desc) desc.textContent = "Shows the mini voice overlay while you are in a game.";
|
|
if (icon) icon.textContent = "sports_esports";
|
|
|
|
if (existingIcon) {
|
|
existingIcon.title = "Toggle gameplay sharing settings";
|
|
existingIcon.style.cursor = "pointer";
|
|
}
|
|
|
|
const settingsBtn = document.createElement("div");
|
|
settingsBtn.title = "Edit gameplay sharing";
|
|
settingsBtn.style.cssText = "cursor: pointer; z-index: 10; flex-shrink: 0; margin-left: 6px;";
|
|
settingsBtn.innerHTML = `
|
|
<div class="fill_var(--md-sys-color-on-surface) bg_var(--md-sys-color-surface-dim) w_36px h_36px d_flex flex-sh_0 ai_center jc_center bdr_var(--borderRadius-full)">
|
|
<span aria-hidden="true" class="material-symbols-outlined fs_inherit fw_undefined!" style="display:block;font-variation-settings:'FILL' 0,'wght' 400,'GRAD' 0;">settings</span>
|
|
</div>
|
|
`;
|
|
|
|
const iconSlot = row.querySelector(".d_flex.ai_center.jc_center, .w_36px");
|
|
if (iconSlot && iconSlot.parentNode) {
|
|
iconSlot.parentNode.appendChild(settingsBtn);
|
|
} else {
|
|
row.appendChild(settingsBtn);
|
|
}
|
|
|
|
const panel = buildPanel();
|
|
const wrapper = document.createElement("div");
|
|
wrapper.style.cssText = "display:flex; flex-direction:column;";
|
|
|
|
const applyState = () => {
|
|
const config = getConfig();
|
|
toggleCheckbox(row, config.gamePresenceEnabled);
|
|
if (config.gamePresenceEnabled) {
|
|
row.setAttribute("data-active", "true");
|
|
} else {
|
|
row.setAttribute("data-active", "false");
|
|
}
|
|
};
|
|
|
|
row.addEventListener("click", (e) => {
|
|
if (settingsBtn.contains(e.target)) return;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const config = getConfig();
|
|
config.gamePresenceEnabled = !config.gamePresenceEnabled;
|
|
setConfig(config);
|
|
applyState();
|
|
});
|
|
|
|
settingsBtn.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
panel.style.display = panel.style.display === "flex" ? "none" : "flex";
|
|
});
|
|
|
|
applyState();
|
|
wrapper.appendChild(row);
|
|
wrapper.appendChild(panel);
|
|
return wrapper;
|
|
}
|
|
|
|
function injectButton() {
|
|
const base = Array.from(document.querySelectorAll("a")).find((e) => {
|
|
const t = e.querySelector("div.d_flex.flex-g_1 > div");
|
|
return t && t.textContent.trim() === "Discord RPC";
|
|
});
|
|
|
|
if (!base) return;
|
|
if (document.querySelector(`[${CLONE_ATTR}]`)) return;
|
|
|
|
const newButton = createButton(base);
|
|
base.parentNode.appendChild(newButton);
|
|
}
|
|
|
|
injectButton();
|
|
|
|
const observer = new MutationObserver(() => injectButton());
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
})();
|