You've already forked Tricky-Addon-Update-Target-List
mirror of
https://github.com/KOWX712/Tricky-Addon-Update-Target-List.git
synced 2025-09-06 06:37:09 +00:00
Invisible module full implementation
NOT STABLE, not tested on KSU
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title data-i18n="title">Document</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="/styles.css" type="text/css">
|
||||
<link rel="stylesheet" type="text/css" href="/mmrl/insets.css" />
|
||||
<script type="module" crossorigin src="/index.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Header -->
|
||||
<div class="header-block"></div>
|
||||
<div class="header">
|
||||
<div id="title" data-i18n="title"></div>
|
||||
<button id="help-button" class="help-button"><i class="fa fa-question-circle"></i></button>
|
||||
<div class="no-connection">
|
||||
<img src="wifi-slash.svg" alt="No Connection Icon" class="wifi-icon">
|
||||
</div>
|
||||
<div class="language-dropdown">
|
||||
<button class="language-button">
|
||||
<i class="fa fa-compass"></i>
|
||||
</button>
|
||||
<div class="language-menu">
|
||||
<button class="language-option" data-lang="en-US">English</button>
|
||||
<button class="language-option" data-lang="ru-RU">Русский</button>
|
||||
<button class="language-option" data-lang="tl-PH">Tagalog</button>
|
||||
<button class="language-option" data-lang="zh-CN">中文(简体)</button>
|
||||
<button class="language-option" data-lang="zh-TW">中文(繁体)</button>
|
||||
</div>
|
||||
<div id="language-overlay" class="language-overlay"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading Element -->
|
||||
<div class="loading" data-i18n="loading"></div>
|
||||
|
||||
<!-- Prompt Element -->
|
||||
<div id="prompt" class="prompt"></div>
|
||||
|
||||
<!-- Floating Button Element -->
|
||||
<div class="floating-card">
|
||||
<button class="floating-btn" id="save" data-i18n="save_and_update_button"></button>
|
||||
</div>
|
||||
|
||||
<!-- Menu Options -->
|
||||
<div class="search-menu-container">
|
||||
<div class="search-card">
|
||||
<span class="search-icon"><i class="fa fa-search"></i></span>
|
||||
<input type="text" class="search-input" id="search" placeholder="Search" data-i18n="search_placeholder">
|
||||
<button class="clear-btn" id="clear-btn">✕</button>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<input type="checkbox" id="menu-toggle" class="menu-toggle">
|
||||
<label for="menu-toggle" id="menu-button">
|
||||
<span class="menu-icon"><i class="fa fa-bars"></i></span>
|
||||
</label>
|
||||
<div id="menu-options" class="menu-options">
|
||||
<ul>
|
||||
<li id="refresh" data-i18n="refresh"></li>
|
||||
<li id="select-all" data-i18n="select_all"></li>
|
||||
<li id="deselect-all" data-i18n="deselect_all"></li>
|
||||
<li id="select-denylist" data-i18n="select_denylist"></li>
|
||||
<li id="deselect-unnecessary" data-i18n="deselect_unnecessary"></li>
|
||||
<li id="aospkb" data-i18n="set_aosp_keybox"></li>
|
||||
<li id="extrakb" data-i18n="set_valid_keybox"></li>
|
||||
<li id="boot-hash" data-i18n="set_verified_boot_hash"></li>
|
||||
<li id="about" data-i18n="about"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="menu-overlay" class="menu-overlay"></div>
|
||||
</div>
|
||||
|
||||
<!-- Applist Display -->
|
||||
<div id="apps-list"></div>
|
||||
<div class="update-card" id="update-card">
|
||||
<p id="update-available" data-i18n="update_available"></p>
|
||||
<p id="redirect-to-release" data-i18n="redirect_to_release"></p>
|
||||
</div>
|
||||
<template id="app-template">
|
||||
<div class="card-box">
|
||||
<div class="card">
|
||||
<div class="content" data-package="">
|
||||
<p class="name"></p>
|
||||
<input type="checkbox" class="checkbox">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Help Overlay -->
|
||||
<div id="help-overlay" class="help-overlay">
|
||||
<div class="help-menu">
|
||||
<button id="close-help" class="close-help">✕</button>
|
||||
<div class="help-content">
|
||||
<p data-i18n="help_instructions"></p>
|
||||
<ul id="help-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BootHash Input Overlay -->
|
||||
<div id="boot-hash-overlay" class="boot-hash-overlay"></div>
|
||||
<div id="boot-hash-card" class="boot-hash-card">
|
||||
<textarea id="boot-hash-input" class="input-box" placeholder="Paste your verified Boot Hash here"
|
||||
data-i18n="boot_hash_input_placeholder"></textarea>
|
||||
<div class="button-container">
|
||||
<button id="boot-hash-save-button" class="boot-hash-save-button" data-i18n="boot_hash_save_button"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- About Overlay -->
|
||||
<div id="about-overlay" class="about-overlay">
|
||||
<div id="about-menu" class="about-menu">
|
||||
<button id="close-about" class="close-about">✕</button>
|
||||
<div class="about-content">
|
||||
<p data-i18n="module_name_line1"></p>
|
||||
<p data-i18n="module_name_line2"></p>
|
||||
<p><span data-i18n="by"></span>KOWX712</p>
|
||||
<br>
|
||||
<p data-i18n="disclaimer"></p>
|
||||
<br>
|
||||
<p>
|
||||
<span id="telegram" data-i18n="telegram_channel"></span><span id="github" data-i18n="github"></span>
|
||||
</p>
|
||||
<br>
|
||||
<p data-i18n="acknowledgment"></p>
|
||||
<p>j-hc/zygisk-detach: WebUI template</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
<div class="uninstall-container hidden-uninstall">
|
||||
<i class="fa fa-trash"></i>
|
||||
<span data-i18n="uninstall_webui"></span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,837 @@
|
||||
// Header Elements
|
||||
const headerBlock = document.querySelector('.header-block');
|
||||
const title = document.querySelector('.header');
|
||||
const helpButton = document.getElementById('help-button');
|
||||
const noConnection = document.querySelector('.no-connection');
|
||||
const languageButton = document.querySelector('.language-button');
|
||||
const languageMenu = document.querySelector('.language-menu');
|
||||
const languageOptions = document.querySelectorAll('.language-option');
|
||||
const languageOverlay = document.getElementById('language-overlay');
|
||||
|
||||
// Help Overlay Elements
|
||||
const helpOverlay = document.getElementById('help-overlay');
|
||||
const closeHelp = document.getElementById('close-help');
|
||||
const helpList = document.getElementById('help-list');
|
||||
|
||||
// Search and Menu Elements
|
||||
const searchInput = document.getElementById('search');
|
||||
const clearBtn = document.getElementById('clear-btn');
|
||||
const searchMenuContainer = document.querySelector('.search-menu-container');
|
||||
const searchCard = document.querySelector('.search-card');
|
||||
const menu = document.querySelector('.menu');
|
||||
const menuButton = document.getElementById('menu-button');
|
||||
const menuOptions = document.getElementById('menu-options');
|
||||
const selectDenylistElement = document.getElementById('select-denylist');
|
||||
const menuOverlay = document.getElementById('menu-overlay');
|
||||
const menuIcon = menuButton.querySelector('.menu-icon');
|
||||
|
||||
// BootHash Overlay Elements
|
||||
const bootHashOverlay = document.getElementById('boot-hash-overlay');
|
||||
const card = document.getElementById('boot-hash-card');
|
||||
const inputBox = document.getElementById('boot-hash-input');
|
||||
const saveButton = document.getElementById('boot-hash-save-button');
|
||||
|
||||
// Applist Elements
|
||||
const appTemplate = document.getElementById('app-template').content;
|
||||
const appListContainer = document.getElementById('apps-list');
|
||||
const updateCard = document.getElementById('update-card');
|
||||
|
||||
// Loading, Save and Prompt Elements
|
||||
const loadingIndicator = document.querySelector('.loading');
|
||||
const floatingBtn = document.querySelector('.floating-btn');
|
||||
const prompt = document.getElementById('prompt');
|
||||
|
||||
// About Elements
|
||||
const telegramLink = document.getElementById('telegram');
|
||||
const githubLink = document.getElementById('github');
|
||||
|
||||
const basePath = "set-path";
|
||||
const ADDITIONAL_APPS = [
|
||||
"com.google.android.gms",
|
||||
"io.github.vvb2060.keyattestation",
|
||||
"io.github.vvb2060.mahoshojo",
|
||||
"icu.nullptr.nativetest"
|
||||
];
|
||||
|
||||
// Variables
|
||||
let e = 0;
|
||||
let excludeList = [];
|
||||
let isRefreshing = false;
|
||||
let translations = {};
|
||||
let currentLang = 'en-US';
|
||||
let availableLanguages = ['en-US'];
|
||||
|
||||
// Function to check for available language
|
||||
async function initializeAvailableLanguages() {
|
||||
try {
|
||||
const multiLang = await execCommand(`find ${basePath}webroot/locales -type f -name "*.json" ! -name "A-template.json" -exec basename -s .json {} \\;`);
|
||||
availableLanguages = multiLang.trim().split('\n');
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch available languages:', error);
|
||||
availableLanguages = ['en-US'];
|
||||
}
|
||||
}
|
||||
|
||||
// Function to detect user's default language
|
||||
function detectUserLanguage() {
|
||||
const userLang = navigator.language || navigator.userLanguage;
|
||||
const langCode = userLang.split('-')[0];
|
||||
if (availableLanguages.includes(userLang)) {
|
||||
return userLang;
|
||||
} else if (availableLanguages.includes(langCode)) {
|
||||
return langCode;
|
||||
} else {
|
||||
return 'en-US';
|
||||
}
|
||||
}
|
||||
|
||||
// Load translations dynamically based on the selected language
|
||||
async function loadTranslations(lang) {
|
||||
try {
|
||||
const response = await fetch(`/locales/${lang}.json`);
|
||||
translations = await response.json();
|
||||
applyTranslations();
|
||||
} catch (error) {
|
||||
console.error(`Error loading translations for ${lang}:`, error);
|
||||
if (lang !== 'en-US') {
|
||||
console.log("Falling back to English.");
|
||||
loadTranslations('en-US');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to apply translations to all elements with data-i18n attributes
|
||||
function applyTranslations() {
|
||||
document.querySelectorAll("[data-i18n]").forEach((el) => {
|
||||
const key = el.getAttribute("data-i18n");
|
||||
if (translations[key]) {
|
||||
if (el.hasAttribute("placeholder")) {
|
||||
el.setAttribute("placeholder", translations[key]);
|
||||
} else {
|
||||
el.textContent = translations[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
updateHelpMenu();
|
||||
}
|
||||
|
||||
// Language selection event listener
|
||||
document.querySelectorAll(".language-option").forEach((button) => {
|
||||
button.addEventListener("click", (e) => {
|
||||
const lang = e.target.getAttribute("data-lang");
|
||||
loadTranslations(lang);
|
||||
});
|
||||
});
|
||||
|
||||
// Function to setup the language menu
|
||||
function setupLanguageMenu() {
|
||||
languageButton.addEventListener("click", (event) => {
|
||||
event.stopPropagation();
|
||||
const isVisible = languageMenu.classList.contains("show");
|
||||
if (isVisible) {
|
||||
closeLanguageMenu();
|
||||
} else {
|
||||
openLanguageMenu();
|
||||
}
|
||||
});
|
||||
document.addEventListener("click", (event) => {
|
||||
if (!languageButton.contains(event.target) && !languageMenu.contains(event.target)) {
|
||||
closeLanguageMenu();
|
||||
}
|
||||
});
|
||||
languageOptions.forEach(option => {
|
||||
option.addEventListener("click", () => {
|
||||
closeLanguageMenu();
|
||||
});
|
||||
});
|
||||
window.addEventListener('scroll', () => {
|
||||
if (languageMenu.classList.contains("show")) {
|
||||
closeLanguageMenu();
|
||||
}
|
||||
});
|
||||
function openLanguageMenu() {
|
||||
languageMenu.classList.add("show");
|
||||
languageOverlay.style.display = 'flex';
|
||||
}
|
||||
function closeLanguageMenu() {
|
||||
languageMenu.classList.remove("show");
|
||||
languageOverlay.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Focus on search input when search card is clicked
|
||||
searchCard.addEventListener("click", () => {
|
||||
searchInput.focus();
|
||||
});
|
||||
|
||||
// Search functionality
|
||||
searchInput.addEventListener("input", (e) => {
|
||||
const searchQuery = e.target.value.toLowerCase();
|
||||
const apps = appListContainer.querySelectorAll(".card");
|
||||
apps.forEach(app => {
|
||||
const name = app.querySelector(".name").textContent.toLowerCase();
|
||||
app.style.display = name.includes(searchQuery) ? "block" : "none";
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
if (searchQuery !== "") {
|
||||
clearBtn.style.display = "block";
|
||||
} else {
|
||||
clearBtn.style.display = "none";
|
||||
}
|
||||
});
|
||||
|
||||
// Clear search input
|
||||
clearBtn.addEventListener("click", () => {
|
||||
searchInput.value = "";
|
||||
clearBtn.style.display = "none";
|
||||
window.scrollTo(0, 0);
|
||||
const apps = appListContainer.querySelectorAll(".card");
|
||||
apps.forEach(app => {
|
||||
app.style.display = "block";
|
||||
});
|
||||
});
|
||||
|
||||
// Function to dynamically update the help menu
|
||||
function updateHelpMenu() {
|
||||
helpList.innerHTML = "";
|
||||
const helpSections = [
|
||||
{ title: "save_and_update_button", description: "save_and_update_description" },
|
||||
{ title: "refresh", description: "refresh_description" },
|
||||
{ title: "select_deselect", description: "select_description" },
|
||||
{ title: "select_denylist", description: "select_denylist_description" },
|
||||
{ title: "deselect_unnecessary", description: "deselect_unnecessary_description" },
|
||||
{ title: "set_keybox", description: "set_aosp_keybox_description" },
|
||||
{ title: "set_verified_boot_hash", description: "set_verified_boot_hash_description" }
|
||||
];
|
||||
helpSections.forEach((section) => {
|
||||
const listItem = document.createElement("li");
|
||||
listItem.innerHTML = `<strong data-i18n="${section.title}">${translations[section.title]}</strong>`;
|
||||
const description = document.createElement("ul");
|
||||
const descriptionItem = document.createElement("li");
|
||||
descriptionItem.textContent = translations[section.description] || `[Missing: ${section.description}]`;
|
||||
description.appendChild(descriptionItem);
|
||||
listItem.appendChild(description);
|
||||
helpList.appendChild(listItem);
|
||||
const emptyLine = document.createElement("li");
|
||||
emptyLine.innerHTML = "<br>";
|
||||
helpList.appendChild(emptyLine);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to setup the help menu
|
||||
function setupHelpOverlay() {
|
||||
helpButton.addEventListener("click", () => {
|
||||
helpOverlay.classList.remove("hide");
|
||||
helpOverlay.style.display = "flex";
|
||||
requestAnimationFrame(() => {
|
||||
helpOverlay.classList.add("show");
|
||||
});
|
||||
document.body.classList.add("no-scroll");
|
||||
});
|
||||
const hideHelpOverlay = () => {
|
||||
helpOverlay.classList.remove("show");
|
||||
helpOverlay.classList.add("hide");
|
||||
document.body.classList.remove("no-scroll");
|
||||
setTimeout(() => {
|
||||
helpOverlay.style.display = "none";
|
||||
}, 200);
|
||||
};
|
||||
closeHelp.addEventListener("click", hideHelpOverlay);
|
||||
helpOverlay.addEventListener("click", (event) => {
|
||||
if (event.target === helpOverlay) {
|
||||
hideHelpOverlay();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Function to toggle menu option
|
||||
function setupMenuToggle() {
|
||||
let menuOpen = false;
|
||||
let menuAnimating = false;
|
||||
menuButton.addEventListener('click', (event) => {
|
||||
if (menuAnimating) return;
|
||||
event.stopPropagation();
|
||||
if (menuOptions.classList.contains('visible')) {
|
||||
closeMenu();
|
||||
} else {
|
||||
openMenu();
|
||||
}
|
||||
});
|
||||
document.addEventListener('click', (event) => {
|
||||
if (!menuOptions.contains(event.target) && event.target !== menuButton) {
|
||||
closeMenu();
|
||||
}
|
||||
});
|
||||
window.addEventListener('scroll', () => {
|
||||
if (menuOptions.classList.contains('visible')) {
|
||||
closeMenu();
|
||||
}
|
||||
});
|
||||
const menuOptionsList = document.querySelectorAll('#menu-options li');
|
||||
menuOptionsList.forEach(option => {
|
||||
option.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
closeMenu();
|
||||
});
|
||||
});
|
||||
function openMenu() {
|
||||
menuAnimating = true;
|
||||
menuOptions.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
menuOptions.classList.remove('hidden');
|
||||
menuOptions.classList.add('visible');
|
||||
menuIcon.classList.add('menu-open');
|
||||
menuIcon.classList.remove('menu-closed');
|
||||
menuOverlay.style.display = 'flex';
|
||||
menuOpen = true;
|
||||
menuAnimating = false;
|
||||
}, 10);
|
||||
}
|
||||
function closeMenu() {
|
||||
if (menuOptions.classList.contains('visible')) {
|
||||
menuAnimating = true;
|
||||
menuOptions.classList.remove('visible');
|
||||
menuOptions.classList.add('hidden');
|
||||
menuIcon.classList.remove('menu-open');
|
||||
menuIcon.classList.add('menu-closed');
|
||||
menuOverlay.style.display = 'none';
|
||||
setTimeout(() => {
|
||||
menuOptions.style.display = 'none';
|
||||
menuOpen = false;
|
||||
menuAnimating = false;
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to refresh app list
|
||||
async function refreshAppList() {
|
||||
isRefreshing = true;
|
||||
title.style.transform = 'translateY(0)';
|
||||
searchMenuContainer.style.transform = 'translateY(0)';
|
||||
floatingBtn.style.transform = 'translateY(0)';
|
||||
searchInput.value = '';
|
||||
clearBtn.style.display = "none";
|
||||
appListContainer.innerHTML = '';
|
||||
loadingIndicator.style.display = 'flex';
|
||||
document.querySelector('.uninstall-container').classList.add('hidden-uninstall');
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
window.scrollTo(0, 0);
|
||||
if (noConnection.style.display === "flex") {
|
||||
await updateCheck();
|
||||
}
|
||||
await fetchAppList();
|
||||
loadingIndicator.style.display = 'none';
|
||||
document.querySelector('.uninstall-container').classList.remove('hidden-uninstall');
|
||||
isRefreshing = false;
|
||||
}
|
||||
|
||||
// Function to select all visible apps
|
||||
function selectAllApps() {
|
||||
document.querySelectorAll(".card").forEach(card => {
|
||||
if (card.style.display !== "none") {
|
||||
card.querySelector(".checkbox").checked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Function to deselect all visible apps
|
||||
function deselectAllApps() {
|
||||
document.querySelectorAll(".card").forEach(card => {
|
||||
if (card.style.display !== "none") {
|
||||
card.querySelector(".checkbox").checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Function to run the update check
|
||||
async function updateCheck() {
|
||||
try {
|
||||
const scriptPath = `sh ${basePath}common/get_extra.sh --update`;
|
||||
const output = await execCommand(scriptPath);
|
||||
console.log("update script executed successfully.");
|
||||
noConnection.style.display = "none";
|
||||
if (output.includes("update")) {
|
||||
console.log("Update detected from extra script.");
|
||||
showPrompt("new_update");
|
||||
updateCard.style.display = "flex";
|
||||
await execCommand(`
|
||||
su -c "mkdir -p '/data/adb/modules/TA_utl' &&
|
||||
cp -rf '${basePath}common/temp/'* '/data/adb/modules/TA_utl/'"
|
||||
`);
|
||||
} else {
|
||||
console.log("No update detected from extra script.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to execute update script:", error);
|
||||
showPrompt("no_internet", false);
|
||||
noConnection.style.display = "flex";
|
||||
}
|
||||
}
|
||||
|
||||
// Function to read the exclude list and uncheck corresponding apps
|
||||
async function deselectUnnecessaryApps() {
|
||||
try {
|
||||
const fileCheck = await execCommand(`test -f ${basePath}common/exclude-list && echo "exists" || echo "not found"`);
|
||||
if (fileCheck.trim() === "not found") {
|
||||
setTimeout(async () => {
|
||||
await execCommand(`sh ${basePath}common/get_extra.sh --unnecessary`);
|
||||
}, 100);
|
||||
console.log("Exclude list not found. Running the unnecessary apps script.");
|
||||
} else {
|
||||
setTimeout(async () => {
|
||||
await execCommand(`sh ${basePath}common/get_extra.sh --xposed`);
|
||||
}, 100);
|
||||
console.log("Exclude list found. Running xposed script.");
|
||||
}
|
||||
const result = await execCommand(`cat ${basePath}common/exclude-list`);
|
||||
const UnnecessaryApps = result.split("\n").map(app => app.trim()).filter(Boolean);
|
||||
const apps = document.querySelectorAll(".card");
|
||||
apps.forEach(app => {
|
||||
const contentElement = app.querySelector(".content");
|
||||
const packageName = contentElement.getAttribute("data-package");
|
||||
const checkbox = app.querySelector(".checkbox");
|
||||
if (UnnecessaryApps.includes(packageName)) {
|
||||
checkbox.checked = false;
|
||||
}
|
||||
});
|
||||
console.log("Unnecessary apps deselected successfully.");
|
||||
} catch (error) {
|
||||
console.error("Failed to deselect unnecessary apps:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to check if Magisk
|
||||
async function checkMagisk() {
|
||||
try {
|
||||
const hasDenylistCondition = await execCommand(`
|
||||
if [ ! -f "/data/adb/apd" ] && [ ! -f "/data/adb/ksud" ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
`);
|
||||
if (hasDenylistCondition.trim() === "OK") {
|
||||
console.log("Denylist conditions met, displaying element.");
|
||||
selectDenylistElement.style.display = "flex";
|
||||
} else {
|
||||
console.log("ksud or apd detected, leaving denylist element hidden.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error while checking denylist conditions:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to read the denylist and check corresponding apps
|
||||
async function selectDenylistApps() {
|
||||
try {
|
||||
const result = await execCommand(`
|
||||
magisk --denylist ls 2>/dev/null | \
|
||||
awk -F'|' '{print $1}' | \
|
||||
grep -v "isolated" | \
|
||||
sort | uniq
|
||||
`);
|
||||
const denylistApps = result.split("\n").map(app => app.trim()).filter(Boolean);
|
||||
const apps = document.querySelectorAll(".card");
|
||||
await deselectAllApps();
|
||||
apps.forEach(app => {
|
||||
const contentElement = app.querySelector(".content");
|
||||
const packageName = contentElement.getAttribute("data-package");
|
||||
const checkbox = app.querySelector(".checkbox");
|
||||
if (denylistApps.includes(packageName)) {
|
||||
checkbox.checked = true; // Select the app if found in denylist
|
||||
}
|
||||
});
|
||||
console.log("Denylist apps selected successfully.");
|
||||
} catch (error) {
|
||||
console.error("Failed to select Denylist apps:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to replace aosp kb
|
||||
async function aospkb() {
|
||||
try {
|
||||
const sourcePath = `${basePath}common/.default`;
|
||||
const destinationPath = "/data/adb/tricky_store/keybox.xml";
|
||||
await execCommand(`xxd -r -p ${sourcePath} | base64 -d > ${destinationPath}`);
|
||||
console.log("AOSP keybox copied successfully.");
|
||||
showPrompt("aosp_key_set");
|
||||
} catch (error) {
|
||||
console.error("Failed to copy AOSP keybox:", error);
|
||||
showPrompt("key_set_error", false);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to replace valid kb
|
||||
async function extrakb() {
|
||||
setTimeout(async () => {
|
||||
await execCommand(`sh ${basePath}common/get_extra.sh --kb`);
|
||||
}, 100);
|
||||
const sourcePath = `${basePath}common/.extra`;
|
||||
const destinationPath = "/data/adb/tricky_store/keybox.xml";
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
const fileExists = await execCommand(`[ -f ${sourcePath} ] && echo "exists"`);
|
||||
if (fileExists.trim() !== "exists") {
|
||||
throw new Error(".extra file not found");
|
||||
}
|
||||
await execCommand(`xxd -r -p ${sourcePath} | base64 -d > ${destinationPath}`);
|
||||
console.log("Valid keybox copied successfully.");
|
||||
showPrompt("valid_key_set");
|
||||
} catch (error) {
|
||||
console.error("Failed to copy valid keybox:", error);
|
||||
await aospkb();
|
||||
showPrompt("no_valid_fallback", false);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to handle Verified Boot Hash
|
||||
async function setBootHash() {
|
||||
const showCard = () => {
|
||||
bootHashOverlay.style.display = "flex";
|
||||
card.style.display = "flex";
|
||||
requestAnimationFrame(() => {
|
||||
bootHashOverlay.classList.add("show");
|
||||
card.classList.add("show");
|
||||
});
|
||||
document.body.style.overflow = "hidden";
|
||||
};
|
||||
const closeCard = () => {
|
||||
bootHashOverlay.classList.remove("show");
|
||||
card.classList.remove("show");
|
||||
setTimeout(() => {
|
||||
bootHashOverlay.style.display = "none";
|
||||
card.style.display = "none";
|
||||
document.body.style.overflow = "auto";
|
||||
}, 200);
|
||||
};
|
||||
showCard();
|
||||
try {
|
||||
const bootHashContent = await execCommand("cat /data/adb/boot_hash");
|
||||
const validHash = bootHashContent
|
||||
.split("\n")
|
||||
.filter(line => !line.startsWith("#") && line.trim())[0];
|
||||
inputBox.value = validHash || "";
|
||||
} catch (error) {
|
||||
console.warn("Failed to read boot_hash file. Defaulting to empty input.");
|
||||
inputBox.value = "";
|
||||
}
|
||||
saveButton.addEventListener("click", async () => {
|
||||
const inputValue = inputBox.value.trim();
|
||||
try {
|
||||
await execCommand(`echo "${inputValue}" > /data/adb/boot_hash`);
|
||||
await execCommand(`su -c resetprop -n ro.boot.vbmeta.digest ${inputValue}`);
|
||||
showPrompt("boot_hash_set");
|
||||
closeCard();
|
||||
} catch (error) {
|
||||
console.error("Failed to update boot_hash:", error);
|
||||
showPrompt("boot_hash_set_error", false);
|
||||
}
|
||||
});
|
||||
bootHashOverlay.addEventListener("click", (event) => {
|
||||
if (event.target === bootHashOverlay) closeCard();
|
||||
});
|
||||
}
|
||||
|
||||
// Function to show about overlay
|
||||
function aboutMenu() {
|
||||
const aboutOverlay = document.getElementById('about-overlay');
|
||||
const aboutMenu = document.getElementById('about-menu');
|
||||
const closeAbout = document.getElementById('close-about');
|
||||
const showMenu = () => {
|
||||
aboutOverlay.style.display = 'flex';
|
||||
setTimeout(() => {
|
||||
aboutOverlay.style.opacity = '1';
|
||||
aboutMenu.style.opacity = '1';
|
||||
}, 10);
|
||||
document.body.style.overflow = 'hidden';
|
||||
};
|
||||
const hideMenu = () => {
|
||||
aboutOverlay.style.opacity = '0';
|
||||
aboutMenu.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
aboutOverlay.style.display = 'none';
|
||||
document.body.style.overflow = 'auto';
|
||||
}, 200);
|
||||
};
|
||||
showMenu();
|
||||
closeAbout.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
hideMenu();
|
||||
});
|
||||
aboutOverlay.addEventListener('click', (event) => {
|
||||
if (!aboutMenu.contains(event.target)) {
|
||||
hideMenu();
|
||||
}
|
||||
});
|
||||
menu.addEventListener('click', (event) => event.stopPropagation());
|
||||
}
|
||||
|
||||
// Fetch and render applist
|
||||
async function fetchAppList() {
|
||||
try {
|
||||
let targetList = [];
|
||||
try {
|
||||
const targetFileContent = await execCommand('cat /data/adb/tricky_store/target.txt');
|
||||
targetList = targetFileContent.split("\n").filter(app => app.trim() !== ''); // Filter out empty lines
|
||||
console.log("Current target list:", targetList);
|
||||
} catch (error) {
|
||||
console.error("Failed to read target.txt file:", error);
|
||||
}
|
||||
|
||||
let applistMap = {};
|
||||
try {
|
||||
const applistResult = await execCommand(`cat ${basePath}common/applist`);
|
||||
applistMap = applistResult
|
||||
.split("\n")
|
||||
.reduce((map, line) => {
|
||||
const match = line.match(/app-name:\s*(.+),\s*package-name:\s*(.+)/);
|
||||
if (match) {
|
||||
const appName = match[1].trim();
|
||||
const packageName = match[2].trim();
|
||||
map[packageName] = appName;
|
||||
}
|
||||
return map;
|
||||
}, {});
|
||||
console.log("Applist loaded successfully.");
|
||||
} catch (error) {
|
||||
console.warn("Applist file not found or could not be loaded. Skipping applist lookup.");
|
||||
}
|
||||
|
||||
const result = await execCommand("pm list packages -3");
|
||||
const appEntries = result
|
||||
.split("\n")
|
||||
.map(line => {
|
||||
const packageName = line.replace("package:", "").trim();
|
||||
const appName = applistMap[packageName] || null;
|
||||
return { appName, packageName };
|
||||
})
|
||||
.filter(entry => entry.packageName);
|
||||
for (const entry of appEntries) {
|
||||
if (!entry.appName) {
|
||||
try {
|
||||
const apkPath = await execCommand(`pm path ${entry.packageName} | grep "base.apk" | awk -F: '{print $2}' | tr -d '\\r'`);
|
||||
if (apkPath) {
|
||||
const appName = await execCommand(`${basePath}common/aapt dump badging ${apkPath.trim()} 2>/dev/null | grep "application-label:" | sed "s/application-label://; s/'//g"`);
|
||||
entry.appName = appName.trim() || "Unknown App";
|
||||
} else {
|
||||
entry.appName = "Unknown App";
|
||||
}
|
||||
} catch (error) {
|
||||
entry.appName = "Unknown App";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort
|
||||
const sortedApps = appEntries.sort((a, b) => {
|
||||
const aChecked = targetList.includes(a.packageName);
|
||||
const bChecked = targetList.includes(b.packageName);
|
||||
if (aChecked !== bChecked) {
|
||||
return aChecked ? -1 : 1;
|
||||
}
|
||||
return (a.appName || "").localeCompare(b.appName || "");
|
||||
});
|
||||
|
||||
// Render
|
||||
appListContainer.innerHTML = "";
|
||||
sortedApps.forEach(({ appName, packageName }) => {
|
||||
const appElement = document.importNode(appTemplate, true);
|
||||
const contentElement = appElement.querySelector(".content");
|
||||
contentElement.setAttribute("data-package", packageName);
|
||||
const nameElement = appElement.querySelector(".name");
|
||||
nameElement.innerHTML = `<strong>${appName || "Unknown App"}</strong><br>${packageName}`;
|
||||
const checkbox = appElement.querySelector(".checkbox");
|
||||
checkbox.checked = targetList.includes(packageName);
|
||||
appListContainer.appendChild(appElement);
|
||||
});
|
||||
console.log("App list with names and packages rendered successfully.");
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch or render app list with names:", error);
|
||||
}
|
||||
floatingBtn.style.transform = "translateY(-120px)";
|
||||
toggleableCheckbox();
|
||||
if (appListContainer.firstChild !== updateCard) {
|
||||
appListContainer.insertBefore(updateCard, appListContainer.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
// Make checkboxes toggleable
|
||||
function toggleableCheckbox() {
|
||||
const appElements = appListContainer.querySelectorAll(".card");
|
||||
appElements.forEach(card => {
|
||||
const content = card.querySelector(".content");
|
||||
const checkbox = content.querySelector(".checkbox");
|
||||
content.addEventListener("click", (event) => {
|
||||
if (event.target !== checkbox) {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Function to show the prompt with a success or error message
|
||||
function showPrompt(key, isSuccess = true) {
|
||||
const message = translations[key] || key;
|
||||
prompt.textContent = message;
|
||||
prompt.classList.toggle('error', !isSuccess);
|
||||
if (window.promptTimeout) {
|
||||
clearTimeout(window.promptTimeout);
|
||||
}
|
||||
setTimeout(() => {
|
||||
prompt.classList.add('visible');
|
||||
prompt.classList.remove('hidden');
|
||||
window.promptTimeout = setTimeout(() => {
|
||||
prompt.classList.remove('visible');
|
||||
prompt.classList.add('hidden');
|
||||
}, 3000);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Save configure
|
||||
document.getElementById("save").addEventListener("click", async () => {
|
||||
const selectedApps = Array.from(appListContainer.querySelectorAll(".checkbox:checked"))
|
||||
.map(checkbox => checkbox.closest(".card").querySelector(".content").getAttribute("data-package"));
|
||||
let finalAppsList = new Set(selectedApps);
|
||||
ADDITIONAL_APPS.forEach(app => {
|
||||
finalAppsList.add(app);
|
||||
});
|
||||
finalAppsList = Array.from(finalAppsList);
|
||||
try {
|
||||
const updatedTargetContent = finalAppsList.join("\n");
|
||||
await execCommand(`echo "${updatedTargetContent}" > /data/adb/tricky_store/target.txt`);
|
||||
console.log("target.txt updated successfully.");
|
||||
showPrompt("saved_target");
|
||||
} catch (error) {
|
||||
console.error("Failed to update target.txt:", error);
|
||||
showPrompt("save_error", false);
|
||||
}
|
||||
await refreshAppList();
|
||||
});
|
||||
|
||||
// Uninstall WebUI
|
||||
document.querySelector(".uninstall-container").addEventListener("click", async () => {
|
||||
try {
|
||||
await execCommand(`
|
||||
su -c "
|
||||
if [ -f '${basePath}action.sh' ]; then
|
||||
if [ -d "/data/adb/modules/TA_utl" ]; then
|
||||
rm -rf "/data/adb/modules/TA_utl"
|
||||
fi
|
||||
cp -rf '${basePath}common/temp' '/data/adb/modules/TA_utl' &&
|
||||
touch '/data/adb/modules/TA_utl/remove'
|
||||
else
|
||||
touch '${basePath}remove'
|
||||
fi
|
||||
"
|
||||
`);
|
||||
showPrompt("uninstall_prompt");
|
||||
} catch (error) {
|
||||
console.error("Failed to execute uninstall command:", error);
|
||||
console.log("Error message:", error.message);
|
||||
showPrompt("uninstall_failed", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Function to check if running in MMRL
|
||||
function adjustHeaderForMMRL() {
|
||||
if (typeof ksu !== 'undefined' && ksu.mmrl) {
|
||||
console.log("Running in MMRL");
|
||||
title.style.top = 'var(--window-inset-top)';
|
||||
const insetTop = getComputedStyle(document.documentElement).getPropertyValue('--window-inset-top');
|
||||
const insetTopValue = parseInt(insetTop, 10);
|
||||
searchMenuContainer.style.top = `${insetTopValue + 40}px`;
|
||||
headerBlock.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll event
|
||||
let lastScrollY = window.scrollY;
|
||||
const scrollThreshold = 40;
|
||||
window.addEventListener('scroll', () => {
|
||||
if (isRefreshing) return;
|
||||
if (window.scrollY > lastScrollY && window.scrollY > scrollThreshold) {
|
||||
title.style.transform = 'translateY(-80px)';
|
||||
headerBlock.style.transform = 'translateY(-80px)';
|
||||
searchMenuContainer.style.transform = 'translateY(-40px)';
|
||||
floatingBtn.style.transform = 'translateY(0)';
|
||||
} else if (window.scrollY < lastScrollY) {
|
||||
headerBlock.style.transform = 'translateY(0)';
|
||||
title.style.transform = 'translateY(0)';
|
||||
searchMenuContainer.style.transform = 'translateY(0)';
|
||||
floatingBtn.style.transform = 'translateY(-120px)';
|
||||
}
|
||||
lastScrollY = window.scrollY;
|
||||
});
|
||||
|
||||
// Initial load
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
adjustHeaderForMMRL();
|
||||
await initializeAvailableLanguages();
|
||||
const userLang = detectUserLanguage();
|
||||
await loadTranslations(userLang);
|
||||
setupMenuToggle();
|
||||
setupLanguageMenu();
|
||||
setupHelpOverlay();
|
||||
document.getElementById("refresh").addEventListener("click", refreshAppList);
|
||||
document.getElementById("select-all").addEventListener("click", selectAllApps);
|
||||
document.getElementById("deselect-all").addEventListener("click", deselectAllApps);
|
||||
document.getElementById("select-denylist").addEventListener("click", selectDenylistApps);
|
||||
document.getElementById("deselect-unnecessary").addEventListener("click", deselectUnnecessaryApps);
|
||||
document.getElementById("aospkb").addEventListener("click", aospkb);
|
||||
document.getElementById("extrakb").addEventListener("click", extrakb);
|
||||
document.getElementById("boot-hash").addEventListener("click", setBootHash);
|
||||
document.getElementById("about").addEventListener("click", aboutMenu);
|
||||
await fetchAppList();
|
||||
checkMagisk();
|
||||
loadingIndicator.style.display = "none";
|
||||
document.querySelector('.uninstall-container').classList.remove('hidden-uninstall');
|
||||
setTimeout(updateCheck, 0);
|
||||
});
|
||||
|
||||
// Redirect to GitHub release page
|
||||
updateCard.addEventListener('click', async () => {
|
||||
try {
|
||||
await execCommand('am start -a android.intent.action.VIEW -d https://github.com/KOWX712/Tricky-Addon-Update-Target-List/releases/latest');
|
||||
} catch (error) {
|
||||
console.error('Error opening GitHub Release link:', error);
|
||||
}
|
||||
});
|
||||
|
||||
telegramLink.addEventListener('click', async () => {
|
||||
try {
|
||||
await execCommand('am start -a android.intent.action.VIEW -d https://t.me/kowchannel');
|
||||
} catch (error) {
|
||||
console.error('Error opening Telegram link:', error);
|
||||
}
|
||||
});
|
||||
|
||||
githubLink.addEventListener('click', async () => {
|
||||
try {
|
||||
await execCommand('am start -a android.intent.action.VIEW -d https://github.com/KOWX712/Tricky-Addon-Update-Target-List');
|
||||
} catch (error) {
|
||||
console.error('Error opening GitHub link:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// Function to execute shell commands
|
||||
async function execCommand(command) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callbackName = `exec_callback_${Date.now()}_${e++}`;
|
||||
window[callbackName] = (errno, stdout, stderr) => {
|
||||
delete window[callbackName];
|
||||
if (errno === 0) {
|
||||
resolve(stdout);
|
||||
} else {
|
||||
console.error(`Error executing command: ${stderr}`);
|
||||
reject(stderr);
|
||||
}
|
||||
};
|
||||
try {
|
||||
ksu.exec(command, "{}", callbackName);
|
||||
} catch (error) {
|
||||
console.error(`Execution error: ${error}`);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"title": "Tricky Addon - Update Target List",
|
||||
"search_placeholder": "Search",
|
||||
"save_and_update_button": "Save",
|
||||
"boot_hash_save_button": "Save",
|
||||
"loading": "Loading...",
|
||||
"boot_hash_input_placeholder": "Paste your verified Boot Hash here",
|
||||
"uninstall_webui": "Uninstall WebUI",
|
||||
|
||||
"update_available": "A new version is ready",
|
||||
"redirect_to_release": "tap to download the latest version",
|
||||
|
||||
"refresh": "Refresh",
|
||||
"select_all": "Select All",
|
||||
"deselect_all": "Deselect All",
|
||||
"select_denylist": "Select From DenyList",
|
||||
"deselect_unnecessary": "Deselect Unnecessary",
|
||||
"set_aosp_keybox": "Set AOSP Keybox",
|
||||
"set_valid_keybox": "Set Valid Keybox",
|
||||
"set_verified_boot_hash": "Set Verified Boot Hash",
|
||||
"about": "About",
|
||||
|
||||
"help_instructions": "Instructions",
|
||||
"save_and_update_description": "Save current configure to target.txt.",
|
||||
"refresh_description": "Refresh app list and exclude list.",
|
||||
"select_deselect": "Select & Deselect All",
|
||||
"select_description": "Select or deselect all apps in the current interface.",
|
||||
"select_denylist_description": "Available in Magisk only, select apps that are in the DenyList. Recommended.",
|
||||
"deselect_unnecessary_description": "Unnecessary category: Xposed module, root manager, root-related apps, and general apps that never check bootloader status. This option requires an Internet connection.",
|
||||
"set_keybox": "Set AOSP & Valid Keybox",
|
||||
"set_aosp_keybox_description": "Replace tricky store keybox.xml. AOSP keybox will be replaced if there's no more valid keybox. Valid keybox option requires Internet connection.",
|
||||
"set_verified_boot_hash_description": "Get verifiedBootHash value from Key Attestation Demo. Fix abnormal boot state by resetting ro.boot.vbmeta.digest.",
|
||||
|
||||
"module_name_line1": "Tricky Addon",
|
||||
"module_name_line2": "Update Target List",
|
||||
"by": "by ",
|
||||
"telegram_channel": "Telegram Channel",
|
||||
"github": "GitHub",
|
||||
"disclaimer": "This module is not a part of the Tricky Store module. DO NOT report any issues to Tricky Store if encountered.",
|
||||
"acknowledgment": "Acknowledgment",
|
||||
|
||||
"no_internet": "Please check your Internet connection",
|
||||
"aosp_key_set": "AOSP keybox set successfully",
|
||||
"key_set_error": "Failed to update keybox",
|
||||
"valid_key_set": "Valid keybox set successfully",
|
||||
"no_valid_fallback": "No valid keybox found, replaced with AOSP keybox.",
|
||||
"boot_hash_set": "Verified Boot Hash saved successfully",
|
||||
"boot_hash_set_error": "Failed to update Verified Boot Hash",
|
||||
"saved_target": "Config saved to target.txt",
|
||||
"save_error": "Failed to save config",
|
||||
"uninstall_prompt": "WebUI will be removed after reboot",
|
||||
"uninstall_failed": "Failed to uninstall WebUI",
|
||||
"new_update": "A new update is available!"
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Translation Guide
|
||||
## Fix Bad Translation
|
||||
1. Fork this repository.
|
||||
2. Find your language string file in `/module/webroot/locales/`.
|
||||
3. Edit the string value with translated incorrectly.
|
||||
4. Create a Pull Request.
|
||||
|
||||
---
|
||||
## Add a New Language
|
||||
### Simple
|
||||
- Contact me in Telegram to add a new translation langauge.
|
||||
|
||||
### Advanced
|
||||
1. Fork this repository.
|
||||
2. Make a copy of `/module/webroot/locales/A-template.json`
|
||||
3. Rename it to `language_code-COUNTRY_CODE.json`, e.g., `en-US.json`.
|
||||
4. Translate the string value inside.
|
||||
5. Add `langauge-option` into `/module/webroot/index.html`.
|
||||
|
||||
Format:
|
||||
```xml
|
||||
<button class="language-option" data-lang="language_code-COUNTRY_CODE">languageName</button>
|
||||
```
|
||||
|
||||
Example:
|
||||
```xml
|
||||
<div class="language-menu">
|
||||
<button class="language-option" data-lang="en-US">English</button>
|
||||
</div>
|
||||
```
|
||||
Finally, Create a Pull Request.
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"title": "Tricky Addon - Update Target List",
|
||||
"search_placeholder": "Search",
|
||||
"save_and_update_button": "Save",
|
||||
"boot_hash_save_button": "Save",
|
||||
"loading": "Loading...",
|
||||
"boot_hash_input_placeholder": "Paste your verified Boot Hash here",
|
||||
"uninstall_webui": "Uninstall WebUI",
|
||||
|
||||
"update_available": "A new version is ready",
|
||||
"redirect_to_release": "tap to download the latest version",
|
||||
|
||||
"refresh": "Refresh",
|
||||
"select_all": "Select All",
|
||||
"deselect_all": "Deselect All",
|
||||
"select_denylist": "Select From DenyList",
|
||||
"deselect_unnecessary": "Deselect Unnecessary",
|
||||
"set_aosp_keybox": "Set AOSP Keybox",
|
||||
"set_valid_keybox": "Set Valid Keybox",
|
||||
"set_verified_boot_hash": "Set Verified Boot Hash",
|
||||
"about": "About",
|
||||
|
||||
"help_instructions": "Instructions",
|
||||
"save_and_update_description": "Save current configure to target.txt.",
|
||||
"refresh_description": "Refresh app list and exclude list.",
|
||||
"select_deselect": "Select & Deselect All",
|
||||
"select_description": "Select or deselect all apps in the current interface.",
|
||||
"select_denylist_description": "Available in Magisk only, select apps that are in the DenyList. Recommended.",
|
||||
"deselect_unnecessary_description": "Unnecessary category: Xposed module, root manager, root-related apps, and general apps that never check bootloader status. This option requires an Internet connection.",
|
||||
"set_keybox": "Set AOSP & Valid Keybox",
|
||||
"set_aosp_keybox_description": "Replace tricky store keybox.xml. AOSP keybox will be replaced if there's no more valid keybox. Valid keybox option requires Internet connection.",
|
||||
"set_verified_boot_hash_description": "Get verifiedBootHash value from Key Attestation Demo. Fix abnormal boot state by resetting ro.boot.vbmeta.digest.",
|
||||
|
||||
"module_name_line1": "Tricky Addon",
|
||||
"module_name_line2": "Update Target List",
|
||||
"by": "by ",
|
||||
"telegram_channel": "Telegram Channel",
|
||||
"github": "GitHub",
|
||||
"disclaimer": "This module is not a part of the Tricky Store module. DO NOT report any issues to Tricky Store if encountered.",
|
||||
"acknowledgment": "Acknowledgment",
|
||||
|
||||
"no_internet": "Please check your Internet connection",
|
||||
"aosp_key_set": "AOSP keybox set successfully",
|
||||
"key_set_error": "Failed to update keybox",
|
||||
"valid_key_set": "Valid keybox set successfully",
|
||||
"no_valid_fallback": "No valid keybox found, replaced with AOSP keybox.",
|
||||
"boot_hash_set": "Verified Boot Hash saved successfully",
|
||||
"boot_hash_set_error": "Failed to update Verified Boot Hash",
|
||||
"saved_target": "Config saved to target.txt",
|
||||
"save_error": "Failed to save config",
|
||||
"uninstall_prompt": "WebUI will be removed after reboot",
|
||||
"uninstall_failed": "Failed to uninstall WebUI",
|
||||
"new_update": "A new update is available!"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"title": "Tricky Addon - Обновить список целей",
|
||||
"search_placeholder": "Поиск",
|
||||
"save_and_update_button": "Сохранить",
|
||||
"boot_hash_save_button": "Сохранить",
|
||||
"loading": "Загрузка...",
|
||||
"boot_hash_input_placeholder": "Вставьте свой проверенный Boot Hash сюда",
|
||||
"uninstall_webui": "Удалить WebUI",
|
||||
|
||||
"update_available": "Доступна новая версия",
|
||||
"redirect_to_release": "нажмите, чтобы скачать последнюю версию",
|
||||
|
||||
"refresh": "Обновить",
|
||||
"select_all": "Выбрать все",
|
||||
"deselect_all": "Отменить выбор всех",
|
||||
"select_denylist": "Выбрать из DenyList",
|
||||
"deselect_unnecessary": "Отменить выбор ненужных",
|
||||
"set_aosp_keybox": "Установить AOSP Keybox",
|
||||
"set_valid_keybox": "Установить действующий Keybox",
|
||||
"set_verified_boot_hash": "Установить Verified Boot Hash",
|
||||
"about": "О программе",
|
||||
|
||||
"help_instructions": "Инструкции",
|
||||
"save_and_update_description": "Сохранить текущую конфигурацию в target.txt.",
|
||||
"refresh_description": "Обновить список приложений и список исключений.",
|
||||
"select_deselect": "Выбрать и отменить выбор всех",
|
||||
"select_description": "Выбрать или отменить выбор всех приложений в текущем интерфейсе.",
|
||||
"select_denylist_description": "Доступно только в Magisk, выберите приложения, которые находятся в DenyList. Рекомендуется.",
|
||||
"deselect_unnecessary_description": "Ненужные категории: модули Xposed, менеджеры root, приложения, связанные с root, и общие приложения, которые никогда не проверяют статус загрузчика. Этот параметр требует подключения к интернету.",
|
||||
"set_keybox": "Установить AOSP и действующий Keybox",
|
||||
"set_aosp_keybox_description": "Замените tricky store keybox.xml. AOSP keybox будет заменен, если не будет найден действующий keybox. Опция с действующим keybox требует подключения к интернету.",
|
||||
"set_verified_boot_hash_description": "Получите значение verifiedBootHash из Key Attestation Demo. Исправьте аномальное состояние загрузки, сбросив ro.boot.vbmeta.digest.",
|
||||
|
||||
"module_name_line1": "Tricky Addon",
|
||||
"module_name_line2": "Обновить список целей",
|
||||
"by": "от ",
|
||||
"telegram_channel": "Канал в Telegram",
|
||||
"github": "GitHub",
|
||||
"disclaimer": "Этот WebUI не является частью Tricky Store, НЕ сообщайте автору Tricky Store о любых возникающих проблемах.",
|
||||
"acknowledgment": "Благодарности",
|
||||
|
||||
"no_internet": "Пожалуйста, проверьте ваше подключение к интернету",
|
||||
"aosp_key_set": "AOSP keybox успешно установлен",
|
||||
"key_set_error": "Не удалось обновить keybox",
|
||||
"valid_key_set": "Действующий keybox успешно установлен",
|
||||
"no_valid_fallback": "Не найден действующий keybox, заменен на AOSP keybox.",
|
||||
"boot_hash_set": "Verified Boot Hash успешно сохранен",
|
||||
"boot_hash_set_error": "Не удалось обновить Verified Boot Hash",
|
||||
"saved_target": "Конфигурация сохранена в target.txt",
|
||||
"save_error": "Не удалось сохранить конфигурацию",
|
||||
"uninstall_prompt": "WebUI будет удален после перезагрузки",
|
||||
"uninstall_failed": "Не удалось удалить WebUI",
|
||||
"new_update": "Доступно новое обновление!"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"title": "Tricky Addon - I-update ang Target List",
|
||||
"search_placeholder": "Maghanap",
|
||||
"save_and_update_button": "I-save",
|
||||
"boot_hash_save_button": "I-save",
|
||||
"loading": "Naglo-load...",
|
||||
"boot_hash_input_placeholder": "I-paste ang iyong verified Boot Hash dito",
|
||||
"uninstall_webui": "I-uninstall ang WebUI",
|
||||
|
||||
"update_available": "Handa na ang bagong bersyon",
|
||||
"redirect_to_release": "i-tap para i-download ang pinakabagong bersyon",
|
||||
|
||||
"refresh": "I-refresh",
|
||||
"select_all": "Piliin Lahat",
|
||||
"deselect_all": "Huwag Pumili ng Lahat",
|
||||
"select_denylist": "Piliin mula sa DenyList",
|
||||
"deselect_unnecessary": "Huwag Pumili ng Hindi Kinakailangan",
|
||||
"set_aosp_keybox": "I-set ang AOSP Keybox",
|
||||
"set_valid_keybox": "I-set ang Valid Keybox",
|
||||
"set_verified_boot_hash": "I-set ang Verified Boot Hash",
|
||||
"about": "Tungkol",
|
||||
|
||||
"help_instructions": "Mga Tagubilin",
|
||||
"save_and_update_description": "I-save ang kasalukuyang configuration sa target.txt.",
|
||||
"refresh_description": "I-refresh ang listahan ng apps at exclude list.",
|
||||
"select_deselect": "Piliin & Huwag Pumili ng Lahat",
|
||||
"select_description": "Piliin o huwag piliin ang lahat ng apps sa kasalukuyang interface.",
|
||||
"select_denylist_description": "Available lang sa Magisk, piliin ang mga app na nasa DenyList. Inirerekomenda.",
|
||||
"deselect_unnecessary_description": "Hindi kinakailangang kategorya: Xposed module, root manager, root-related apps, at mga karaniwang apps na hindi kailanman nire-refresh ang bootloader status. Nangangailangan ng koneksyon sa internet.",
|
||||
"set_keybox": "I-set ang AOSP at Valid Keybox",
|
||||
"set_aosp_keybox_description": "Palitan ang tricky store keybox.xml. Palitan ang AOSP keybox kung walang valid keybox. Nangangailangan ng koneksyon sa internet ang valid keybox option.",
|
||||
"set_verified_boot_hash_description": "Kunin ang verifiedBootHash mula sa Key Attestation Demo. Ayusin ang abnormal na boot state sa pamamagitan ng pag-reset ng ro.boot.vbmeta.digest.",
|
||||
|
||||
"module_name_line1": "Tricky Addon",
|
||||
"module_name_line2": "I-update ang Target List",
|
||||
"by": "ni ",
|
||||
"telegram_channel": "Telegram Channel",
|
||||
"github": "GitHub",
|
||||
"disclaimer": "Ang WebUI na ito ay hindi bahagi ng Tricky Store, HUWAG i-report sa may-akda ng Tricky Store kung makaranas ka ng anumang isyu.",
|
||||
"acknowledgment": "Pagkilala",
|
||||
|
||||
"no_internet": "Pakitingnan ang iyong koneksyon sa Internet",
|
||||
"aosp_key_set": "Matagumpay na na-set ang AOSP Keybox",
|
||||
"key_set_error": "Nabigong i-update ang keybox",
|
||||
"valid_key_set": "Matagumpay na na-set ang Valid Keybox",
|
||||
"no_valid_fallback": "Walang valid na keybox na natagpuan, pinalitan ng AOSP keybox.",
|
||||
"boot_hash_set": "Matagumpay na na-save ang Verified Boot Hash",
|
||||
"boot_hash_set_error": "Nabigong i-update ang Verified Boot Hash",
|
||||
"saved_target": "Na-save ang configuration sa target.txt",
|
||||
"save_error": "Nabigong i-save ang config",
|
||||
"uninstall_prompt": "Mawawala ang WebUI pagkatapos ng reboot",
|
||||
"uninstall_failed": "Nabigong i-uninstall ang WebUI",
|
||||
"new_update": "May bagong update na available!"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"title": "TS插件 - 更新目标列表",
|
||||
"search_placeholder": "搜索",
|
||||
"save_and_update_button": "保存",
|
||||
"boot_hash_save_button": "保存",
|
||||
"loading": "加载中...",
|
||||
"boot_hash_input_placeholder": "在此粘贴您的哈希值",
|
||||
"uninstall_webui": "卸载 WebUI",
|
||||
|
||||
"update_available": "发现新的版本",
|
||||
"redirect_to_release": "点击跳转 GitHub 下载最新版本",
|
||||
|
||||
"refresh": "刷新",
|
||||
"select_all": "全选",
|
||||
"deselect_all": "取消全选",
|
||||
"select_denylist": "从排除列表中选择",
|
||||
"deselect_unnecessary": "取消选择非必应用",
|
||||
"set_aosp_keybox": "设置 AOSP 密钥",
|
||||
"set_valid_keybox": "设置有效密钥",
|
||||
"set_verified_boot_hash": "设置哈希值",
|
||||
"about": "关于",
|
||||
|
||||
"help_instructions": "使用指南",
|
||||
"save_and_update_description": "保存当前配置到目标列表(target.txt)。",
|
||||
"refresh_description": "刷新应用列表和排除列表。",
|
||||
"select_deselect": "全选 & 取消全选",
|
||||
"select_description": "选择或取消选择当前界面中的所有应用。",
|
||||
"select_denylist_description": "仅适用于 Magisk,选择在排除列表中的应用。推荐使用。",
|
||||
"deselect_unnecessary_description": "非必要分类:Xposed 模块、root 管理器、与 root 相关的应用,以及从不检查 bootloader 状态的通用应用。此功能需连网使用。",
|
||||
"set_keybox": "设置 AOSP & 有效密钥",
|
||||
"set_aosp_keybox_description": "替换 Tricky Store 的密钥(keybox.xml)。如果没有有效密钥,将替换为 AOSP 密钥。有效密钥选项需连网使用。",
|
||||
"set_verified_boot_hash_description": "从 Key Attestation Demo 获取 verifiedBootHash(哈希值)。通过重置 ro.boot.vbmeta.digest 修复异常 boot 状态。",
|
||||
|
||||
"module_name_line1": "TS插件",
|
||||
"module_name_line2": "更新目标列表",
|
||||
"by": "作者:",
|
||||
"telegram_channel": "TG频道",
|
||||
"github": "GitHub",
|
||||
"disclaimer": "此 WebUI 不是 Tricky Store 的一部分,遇到任何问题请勿向 Tricky Store 作者反馈。",
|
||||
"acknowledgment": "特别鸣谢",
|
||||
|
||||
"no_internet": "请检查您的网络连接",
|
||||
"aosp_key_set": "成功设置 AOSP 密钥",
|
||||
"key_set_error": "更新密钥失败",
|
||||
"valid_key_set": "成功设置有效密钥",
|
||||
"no_valid_fallback": "未找到有效密钥,已替换为 AOSP 密钥。",
|
||||
"boot_hash_set": "哈希值重置成功",
|
||||
"boot_hash_set_error": "哈希值重置失败",
|
||||
"saved_target": "成功保存配置",
|
||||
"save_error": "保存配置失败",
|
||||
"uninstall_prompt": "WebUI 将在重启后被移除",
|
||||
"uninstall_failed": "卸载 WebUI 失败",
|
||||
"new_update": "发现新的版本!"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"title": "TS插件 - 更新目標列表",
|
||||
"search_placeholder": "搜尋",
|
||||
"save_and_update_button": "保存",
|
||||
"boot_hash_save_button": "保存",
|
||||
"loading": "加載中...",
|
||||
"boot_hash_input_placeholder": "在此粘貼您的哈希值",
|
||||
"uninstall_webui": "卸載 WebUI",
|
||||
|
||||
"update_available": "發現新的版本",
|
||||
"redirect_to_release": "點擊跳轉至 GitHub 下載最新版本",
|
||||
|
||||
"refresh": "刷新",
|
||||
"select_all": "全選",
|
||||
"deselect_all": "取消全選",
|
||||
"select_denylist": "從排除列表中選擇",
|
||||
"deselect_unnecessary": "取消選擇非必應用",
|
||||
"set_aosp_keybox": "設置 AOSP 密鑰",
|
||||
"set_valid_keybox": "設置有效密鑰",
|
||||
"set_verified_boot_hash": "設置哈希值",
|
||||
"about": "關於",
|
||||
|
||||
"help_instructions": "使用指南",
|
||||
"save_and_update_description": "保存當前配置到目標列表(target.txt)。",
|
||||
"refresh_description": "刷新應用列表和排除列表。",
|
||||
"select_deselect": "全選 & 取消全選",
|
||||
"select_description": "選擇或取消選擇當前界面中的所有應用。",
|
||||
"select_denylist_description": "僅適用於 Magisk,選擇在排除列表中的應用。推薦使用。",
|
||||
"deselect_unnecessary_description": "非必要分類:Xposed 模塊、root 管理器、與 root 相關的應用,以及從不檢查 bootloader 狀態的通用應用。此功能需連網使用。",
|
||||
"set_keybox": "設置 AOSP & 有效密鑰",
|
||||
"set_aosp_keybox_description": "替換 Tricky Store 的密鑰(keybox.xml)。如果沒有有效密鑰,將替換為 AOSP 密鑰。有效密鑰選項需連網使用。",
|
||||
"set_verified_boot_hash_description": "從 Key Attestation Demo 獲取 verifiedBootHash(哈希值)。通過重置 ro.boot.vbmeta.digest 修復異常 boot 狀態。",
|
||||
|
||||
"module_name_line1": "TS插件",
|
||||
"module_name_line2": "更新目標列表",
|
||||
"by": "作者:",
|
||||
"telegram_channel": "TG頻道",
|
||||
"github": "GitHub",
|
||||
"disclaimer": "此 WebUI 不是 Tricky Store 的一部分,遇到任何問題請勿向 Tricky Store 作者反饋。",
|
||||
"acknowledgment": "特別鳴謝",
|
||||
|
||||
"no_internet": "請檢查您的網路連接",
|
||||
"aosp_key_set": "成功設置 AOSP 密鑰",
|
||||
"key_set_error": "更新密鑰失敗",
|
||||
"valid_key_set": "成功設置有效密鑰",
|
||||
"no_valid_fallback": "未找到有效密鑰,已替換為 AOSP 密鑰。",
|
||||
"boot_hash_set": "哈希值重置成功",
|
||||
"boot_hash_set_error": "哈希值重置失敗",
|
||||
"saved_target": "成功保存配置",
|
||||
"save_error": "保存配置失敗",
|
||||
"uninstall_prompt": "WebUI 將在重啟後被移除",
|
||||
"uninstall_failed": "卸載 WebUI 失敗",
|
||||
"new_update": "發現新的版本!"
|
||||
}
|
||||
@@ -0,0 +1,833 @@
|
||||
body {
|
||||
background-color: #F5F5F5;
|
||||
padding-top: var(--window-inset-top);
|
||||
padding-bottom: var(--window-inset-bottom);
|
||||
}
|
||||
|
||||
.no-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
height: 40px;
|
||||
width: calc(100% - 10px);
|
||||
max-width: 1100px;
|
||||
background-color: #F5F5F5;
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 1100;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.header-block {
|
||||
background-color: #F5F5F5;
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 1100;
|
||||
transition: transform 0.3s ease;
|
||||
height: var(--window-inset-top);
|
||||
}
|
||||
|
||||
#title {
|
||||
padding-left: 5px;
|
||||
font-size: 16.5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.no-connection {
|
||||
padding-bottom: 2px;
|
||||
display: none;
|
||||
position: relative;
|
||||
margin-right: 0px;
|
||||
color: #7E7E7E;
|
||||
}
|
||||
|
||||
.no-connection .wifi-icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
filter: invert(0.6) sepia(0) saturate(0) hue-rotate(180deg) brightness(0.8) contrast(1);
|
||||
}
|
||||
|
||||
.language-dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.language-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 23px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.language-menu {
|
||||
display: flex;
|
||||
padding: 3px 10px;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1800;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
max-height: calc(100vh - 50px);
|
||||
overflow-y: auto;
|
||||
transform: translateY(-10px);
|
||||
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
|
||||
}
|
||||
|
||||
.language-menu.show {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.language-option {
|
||||
padding: 8px 5px;
|
||||
text-align: left;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
border-bottom: 1px solid #ccc;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.language-option:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.language-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: none;
|
||||
z-index: 1100;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.help-button {
|
||||
margin-right: auto;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 22px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.help-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 2000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.help-overlay.show {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.help-overlay.hide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.help-menu {
|
||||
position: relative;
|
||||
width: 75vw;
|
||||
max-width: 800px;
|
||||
background-color: white;
|
||||
padding: 0 10px;
|
||||
border-radius: 15px;
|
||||
text-align: left;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.close-help {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 12px;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 20px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.help-content {
|
||||
max-height: 85vh;
|
||||
padding: 0 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.help-content p {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.help-content ul {
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.help-content ul li {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.help-content ul ul li {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.help-content ul ul ul li {
|
||||
color: #777777;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.help-content ul ul ul li a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.boot-hash-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1200;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.2s ease, visibility 0.2s ease;
|
||||
}
|
||||
|
||||
.boot-hash-card {
|
||||
position: fixed;
|
||||
top: 30%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 80vw;
|
||||
max-width: 600px;
|
||||
background-color: #fff;
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1200;
|
||||
padding: 20px;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.boot-hash-overlay.show {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.boot-hash-card.show {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.input-box {
|
||||
width: calc(100% - 20px);
|
||||
height: 100px;
|
||||
resize: none;
|
||||
padding: 9px;
|
||||
font-size: 16px;
|
||||
background-color: #FFF;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.boot-hash-save-button {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 38px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
margin-left: auto;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.about-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1100;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.about-menu {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 75vw;
|
||||
max-width: 800px;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 25px 30px;
|
||||
z-index: 1200;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1200;
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.close-about {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 12px;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.about-content p {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.about-content p[data-i18n="module_name_line1"] {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.about-content p[data-i18n="module_name_line2"] {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.about-content p span[data-i18n="by"] {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.about-content p[data-i18n="disclaimer"] {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.about-content p[data-i18n="acknowledgment"] {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.about-content p:not([data-i18n]) {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#telegram {
|
||||
font-weight: bold;
|
||||
padding: 5px 10px;
|
||||
background-color: #38A7ED;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
margin-right: 5px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
#github {
|
||||
font-weight: bold;
|
||||
padding: 5px 10px;
|
||||
background-color: #1E1E1E;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.search-menu-container {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
top: 40px;
|
||||
height: 50px;
|
||||
width: calc(100% - 20px);
|
||||
max-width: 1100px;
|
||||
z-index: 1000;
|
||||
transition: transform 0.3s ease;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.search-card {
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 50px;
|
||||
left: 0;
|
||||
height: calc(100% - 2px);
|
||||
width: calc(100% - 60px);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
font-size: 15px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
position: absolute;
|
||||
border: none;
|
||||
font-size: 17px;
|
||||
outline: none;
|
||||
left: 10px;
|
||||
padding: 0 30px;
|
||||
width: calc(100% - 10);
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
position: absolute;
|
||||
color: #ccc;
|
||||
padding-bottom: 3px;
|
||||
right: 10px;
|
||||
border: none;
|
||||
background: none;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#menu-button {
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
width: 48px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
display: inline-block;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.menu-icon.menu-open {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.menu-icon.menu-closed {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.menu-options {
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
position: absolute;
|
||||
padding: 5px 12px;
|
||||
top: 110%;
|
||||
right: 0;
|
||||
z-index: 1200;
|
||||
transform: translateX(120%);
|
||||
transition: transform 0.2s ease;
|
||||
width: auto;
|
||||
max-height: calc(100vh - 120px);
|
||||
overflow-y: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#select-denylist {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-options.visible {
|
||||
display: block;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.menu-options.hidden {
|
||||
transform: translateX(140%);
|
||||
}
|
||||
|
||||
.menu-options ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu-options li {
|
||||
cursor: default;
|
||||
padding: 12px 4px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ccc;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.menu-options li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.menu-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: none;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#apps-list {
|
||||
margin-top: 100px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.update-card {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #DCDCDC;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 10px;
|
||||
outline: none;
|
||||
padding: 12px;
|
||||
width: calc(100% - 30px);
|
||||
max-width: 900px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
#update-available {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#redirect-to-release {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 10px;
|
||||
outline: none;
|
||||
padding: 12px;
|
||||
width: calc(100% - 30px);
|
||||
max-width: 900px;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.name {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
font-size: 15.5px;
|
||||
max-width: calc(100% - 30px);
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin-left: auto;
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.prompt {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 10px;
|
||||
background-color: #4CAF50;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
color: white;
|
||||
font-size: 15px;
|
||||
padding: 5px 15px;
|
||||
z-index: 2000;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.5s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.prompt.visible {
|
||||
animation: YbounceIn 0.4s forwards;
|
||||
}
|
||||
|
||||
.prompt.hidden {
|
||||
animation: YbounceOut 0.4s forwards;
|
||||
}
|
||||
|
||||
.prompt.error {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
@keyframes YbounceIn {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-80%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(-60%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes YbounceOut {
|
||||
0% {
|
||||
transform: translateY(-60%);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-80%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.floating-card {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
bottom: -70px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.floating-btn {
|
||||
flex-shrink: 0;
|
||||
background-color: #007bff;
|
||||
border: none;
|
||||
box-shadow: 0 4px 8px #0003;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
transition: transform 0.3s ease-in-out, background-color 0.2s ease;
|
||||
border-top-left-radius: 50px;
|
||||
border-top-right-radius: 50px;
|
||||
border-bottom-left-radius: 50px;
|
||||
border-bottom-right-radius: 50px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
color: #6E6E6E;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: calc(100% - 10px);
|
||||
max-width: 1100px;
|
||||
padding: 25px 0;
|
||||
position: relative;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.uninstall-container {
|
||||
padding: 10px 10px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 8px;
|
||||
background-color: #CE0000;
|
||||
white-space: nowrap;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.uninstall-container i {
|
||||
margin-right: 5px;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.uninstall-container span {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.uninstall-container.hidden-uninstall {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.language-option:active,
|
||||
.menu-options li:active,
|
||||
.card:active,
|
||||
.update-card:active {
|
||||
background-color: #C8C8C8;
|
||||
}
|
||||
|
||||
.boot-hash-save-button:active,
|
||||
.floating-btn:active {
|
||||
background-color: #003d80;
|
||||
}
|
||||
|
||||
.uninstall-container:active {
|
||||
background-color: #830000;
|
||||
}
|
||||
|
||||
#telegram:active {
|
||||
background-color: #1A78B3;
|
||||
}
|
||||
|
||||
#github:active {
|
||||
background-color: #585858;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #121212;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.header-block,
|
||||
.header {
|
||||
background-color: #121212;
|
||||
}
|
||||
|
||||
.language-button,
|
||||
.language-option,
|
||||
.input-box,
|
||||
.help-button {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.help-menu,
|
||||
.about-menu,
|
||||
.boot-hash-card,
|
||||
.card,
|
||||
.search-input,
|
||||
.search-card {
|
||||
background-color: #343434;
|
||||
}
|
||||
|
||||
.update-card {
|
||||
background-color: #4D4D4D;
|
||||
}
|
||||
|
||||
.search-card {
|
||||
border: 1px solid #6E6E6E;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.language-menu,
|
||||
.input-box,
|
||||
.menu-options,
|
||||
#menu-button {
|
||||
background-color: #343434;
|
||||
border: 1px solid #6E6E6E;
|
||||
}
|
||||
|
||||
.language-option,
|
||||
.menu-options li {
|
||||
border-bottom: 1px solid #6E6E6E;
|
||||
}
|
||||
|
||||
.language-option:active,
|
||||
.menu-options li:active,
|
||||
.card:active,
|
||||
.update-card:active {
|
||||
background-color: #616161;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.92,5.51h0L3.71,2.29A1,1,0,0,0,2.29,3.71L4.56,6A15.21,15.21,0,0,0,1.4,8.39a1,1,0,0,0,0,1.41,1,1,0,0,0,.71.3,1,1,0,0,0,.7-.29A13.07,13.07,0,0,1,6.05,7.46L7.54,9a10.78,10.78,0,0,0-3.32,2.27,1,1,0,1,0,1.42,1.4,8.8,8.8,0,0,1,3.45-2.12l1.62,1.61a7.07,7.07,0,0,0-3.66,1.94,1,1,0,1,0,1.42,1.4A5,5,0,0,1,12,14a4.13,4.13,0,0,1,.63.05l7.66,7.66a1,1,0,0,0,1.42,0,1,1,0,0,0,0-1.42ZM12,16a3,3,0,1,0,3,3A3,3,0,0,0,12,16Zm0,4a1,1,0,1,1,1-1A1,1,0,0,1,12,20ZM22.61,8.39A15,15,0,0,0,10.29,4.1a1,1,0,1,0,.22,2A13.07,13.07,0,0,1,21.2,9.81a1,1,0,0,0,1.41-1.42Zm-4.25,4.24a1,1,0,0,0,1.42-1.4,10.75,10.75,0,0,0-4.84-2.82,1,1,0,1,0-.52,1.92A8.94,8.94,0,0,1,18.36,12.63Z"/></svg>
|
||||
|
After Width: | Height: | Size: 725 B |
Reference in New Issue
Block a user