opt: use ksu.spawn for long script

This commit is contained in:
KOWX712
2025-05-05 18:57:24 +08:00
parent 59a74e8ee2
commit 8c4f7c0e5c
8 changed files with 248 additions and 206 deletions

View File

@@ -1,4 +1,7 @@
#!/bin/sh #!/bin/sh
# This file is the backend of JavaScript
MODPATH=${0%/*} MODPATH=${0%/*}
ORG_PATH="$PATH" ORG_PATH="$PATH"
SKIPLIST="$MODPATH/tmp/skiplist" SKIPLIST="$MODPATH/tmp/skiplist"
@@ -17,13 +20,11 @@ aapt() { "$MODPATH/aapt" "$@"; }
# wget = low pref, no ssl. # wget = low pref, no ssl.
# curl, has ssl on android, we use it if found # curl, has ssl on android, we use it if found
download() { download() {
PATH=/data/adb/ap/bin:/data/adb/ksu/bin:/data/adb/magisk:/data/data/com.termux/files/usr/bin:$PATH
if command -v curl >/dev/null 2>&1; then if command -v curl >/dev/null 2>&1; then
timeout 10 curl -Ls "$1" timeout 10 curl -Ls "$1"
else else
timeout 10 busybox wget --no-check-certificate -qO- "$1" timeout 10 busybox wget --no-check-certificate -qO- "$1"
fi fi
PATH="$ORG_PATH"
} }
get_xposed() { get_xposed() {
@@ -39,6 +40,19 @@ get_xposed() {
cat "$XPOSED" cat "$XPOSED"
} }
get_applist() {
pm list packages -3 | awk -F: '{print $2}'
[ -s "/data/adb/tricky_store/system_app" ] && SYSTEM_APP=$(cat "/data/adb/tricky_store/system_app" | tr '\n' '|' | sed 's/|*$//') || SYSTEM_APP=""
[ -z "$SYSTEM_APP" ] || pm list packages -s | awk -F: '{print $2}' | grep -Ex "$SYSTEM_APP" || true
}
get_appname() {
base_apk=$(pm path $package_name | head -n1 | awk -F: '{print $2}')
app_name=$(aapt dump badging $base_apk 2>/dev/null | grep "application-label:" | sed "s/application-label://; s/'//g")
[ -z "$app_name" ] && app_name="$package_name"
echo "$app_name"
}
check_update() { check_update() {
[ -f "$MODDIR/disable" ] && rm -f "$MODDIR/disable" [ -f "$MODDIR/disable" ] && rm -f "$MODDIR/disable"
LOCAL_VERSION=$(grep '^versionCode=' "$MODPATH/update/module.prop" | awk -F= '{print $2}') LOCAL_VERSION=$(grep '^versionCode=' "$MODPATH/update/module.prop" | awk -F= '{print $2}')
@@ -68,7 +82,6 @@ get_update() {
} }
install_update() { install_update() {
PATH=/data/adb/ap/bin:/data/adb/ksu/bin:/data/adb/magisk:$PATH
if command -v magisk >/dev/null 2>&1; then if command -v magisk >/dev/null 2>&1; then
magisk --install-module "$MODPATH/tmp/module.zip" || exit 1 magisk --install-module "$MODPATH/tmp/module.zip" || exit 1
elif command -v apd >/dev/null 2>&1; then elif command -v apd >/dev/null 2>&1; then
@@ -133,6 +146,15 @@ case "$1" in
get_xposed get_xposed
exit exit
;; ;;
--applist)
get_applist
exit
;;
--appname)
package_name="$2"
get_appname
exit
;;
--check-update) --check-update)
REMOTE_VERSION="$2" REMOTE_VERSION="$2"
check_update check_update

View File

@@ -1,41 +1,42 @@
import { exec, toast } from './assets/kernelsu.js'; import { exec, spawn, toast } from './assets/kernelsu.js';
import { basePath, hideFloatingBtn, appsWithExclamation, appsWithQuestion } from './main.js'; import { basePath, loadingIndicator, hideFloatingBtn, appsWithExclamation, appsWithQuestion, applyRippleEffect } from './main.js';
const appTemplate = document.getElementById('app-template').content; const appTemplate = document.getElementById('app-template').content;
const modeOverlay = document.querySelector('.mode-overlay'); const modeOverlay = document.querySelector('.mode-overlay');
export const appListContainer = document.getElementById('apps-list'); export const appListContainer = document.getElementById('apps-list');
export const updateCard = document.getElementById('update-card'); export const updateCard = document.getElementById('update-card');
let targetList = [];
// Fetch and render applist // Fetch and render applist
export async function fetchAppList() { export async function fetchAppList() {
try { // fetch target list
// fetch target list await exec('cat /data/adb/tricky_store/target.txt')
let targetList = []; .then(({ errno, stdout }) => {
await exec('cat /data/adb/tricky_store/target.txt') if (errno === 0) {
.then(({ errno, stdout }) => { targetList = processTargetList(stdout);
if (errno === 0) { } else {
targetList = processTargetList(stdout); toast("Failed to read target.txt!");
} else { }
toast("Failed to read target.txt!"); });
}
});
// fetch applist // Fetch cached applist
const response = await fetch('applist.json'); const response = await fetch('applist.json');
const appList = await response.json(); const appList = await response.json();
const appNameMap = appList.reduce((map, app) => { const appNameMap = appList.reduce((map, app) => {
map[app.package_name] = app.app_name; map[app.package_name] = app.app_name;
return map; return map;
}, {}); }, {});
// Get installed packages first // Get installed packages
let appEntries = [], installedPackages = []; let appEntries = [], installedPackages = [];
const { stdout } = await exec(` const output = spawn('sh', [`${basePath}/common/get_extra.sh`, '--applist']);
pm list packages -3 | awk -F: '{print $2}' output.stdout.on('data', (data) => {
[ -s "/data/adb/tricky_store/system_app" ] && SYSTEM_APP=$(cat "/data/adb/tricky_store/system_app" | tr '\n' '|' | sed 's/|*$//') || SYSTEM_APP="" if (data.trim() === "") return;
[ -z "$SYSTEM_APP" ] || pm list packages -s | awk -F: '{print $2}' | grep -Ex "$SYSTEM_APP" || true installedPackages.push(data);
`); });
installedPackages = stdout.split("\n").map(line => line.trim()).filter(Boolean); output.on('exit', async () => {
// Create appEntries array contain { appName, packageName }
appEntries = await Promise.all(installedPackages.map(async (packageName) => { appEntries = await Promise.all(installedPackages.map(async (packageName) => {
if (appNameMap[packageName]) { if (appNameMap[packageName]) {
return { return {
@@ -43,76 +44,86 @@ export async function fetchAppList() {
packageName packageName
}; };
} }
const { stdout: appName } = await exec(` return new Promise((resolve) => {
base_apk=$(pm path ${packageName} | head -n1 | awk -F: '{print $2}') const output = spawn('sh', [`${basePath}/common/get_extra.sh`, '--appname', packageName],
aapt dump badging $base_apk 2>/dev/null | grep "application-label:" | sed "s/application-label://; s/'//g" { env: { PATH: `$PATH:${basePath}/common:/data/data/com.termux/files/usr/bin` } });
`, { env: { PATH: `$PATH:${basePath}/common:/data/data/com.termux/files/usr/bin` } }); output.stdout.on('data', (data) => {
return { resolve({
appName: appName || packageName, appName: data,
packageName packageName
}; });
})); });
// 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);
// Set unique names for radio button groups
const radioButtons = appElement.querySelectorAll('input[type="radio"]');
radioButtons.forEach((radio) => {
radio.name = `mode-radio-${packageName}`;
}); });
}));
renderAppList(appEntries);
});
}
// Preselect the radio button based on the package name /**
const generateRadio = appElement.querySelector('#generate-mode'); * Render processed app list to the UI
const hackRadio = appElement.querySelector('#hack-mode'); * @param {Array} data - Array of objects containing appName and packageName
const normalRadio = appElement.querySelector('#normal-mode'); * @returns {void}
*/
function renderAppList(data) {
// Sort
const sortedApps = data.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 || "");
});
if (appsWithExclamation.includes(packageName)) { // Render
generateRadio.checked = true; appListContainer.innerHTML = "";
} else if (appsWithQuestion.includes(packageName)) { sortedApps.forEach(({ appName, packageName }) => {
hackRadio.checked = true; const appElement = document.importNode(appTemplate, true);
} else { const contentElement = appElement.querySelector(".content");
normalRadio.checked = true; contentElement.setAttribute("data-package", packageName);
}
const nameElement = appElement.querySelector(".name"); // Set unique names for radio button groups
nameElement.innerHTML = ` const radioButtons = appElement.querySelectorAll('input[type="radio"]');
<div class="app-info"> radioButtons.forEach((radio) => {
<div class="app-name"><strong>${appName}</strong></div> radio.name = `mode-radio-${packageName}`;
<div class="package-name">${packageName}</div>
</div>
`;
const checkbox = appElement.querySelector(".checkbox");
checkbox.checked = targetList.includes(packageName);
appListContainer.appendChild(appElement);
}); });
} catch (error) {
toast("Failed to fetch app list!"); // Preselect the radio button based on the package name
console.error("Failed to fetch or render app list with names:", error); const generateRadio = appElement.querySelector('#generate-mode');
} const hackRadio = appElement.querySelector('#hack-mode');
const normalRadio = appElement.querySelector('#normal-mode');
if (appsWithExclamation.includes(packageName)) {
generateRadio.checked = true;
} else if (appsWithQuestion.includes(packageName)) {
hackRadio.checked = true;
} else {
normalRadio.checked = true;
}
const nameElement = appElement.querySelector(".name");
nameElement.innerHTML = `
<div class="app-info">
<div class="app-name"><strong>${appName}</strong></div>
<div class="package-name">${packageName}</div>
</div>
`;
const checkbox = appElement.querySelector(".checkbox");
checkbox.checked = targetList.includes(packageName);
appListContainer.appendChild(appElement);
});
loadingIndicator.style.display = "none";
hideFloatingBtn(false); hideFloatingBtn(false);
document.querySelector('.uninstall-container').classList.remove('hidden-uninstall');
toggleableCheckbox(); toggleableCheckbox();
if (appListContainer.firstChild !== updateCard) { if (appListContainer.firstChild !== updateCard) {
appListContainer.insertBefore(updateCard, appListContainer.firstChild); appListContainer.insertBefore(updateCard, appListContainer.firstChild);
} }
const checkboxes = appListContainer.querySelectorAll(".checkbox");
setupRadioButtonListeners(); setupRadioButtonListeners();
setupModeMenu(); setupModeMenu();
updateCheckboxColor(); updateCheckboxColor();
applyRippleEffect();
} }
// Function to save app with ! and ? then process target list // Function to save app with ! and ? then process target list

View File

@@ -41,6 +41,27 @@ export function exec(command, options = {}) {
}); });
} }
/**
* Standard I/O stream for a child process.
* @class
*/
class Stdio {
constructor() {
this.listeners = {};
}
on(event, listener) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(listener);
}
emit(event, ...args) {
if (this.listeners[event]) {
this.listeners[event].forEach(listener => listener(...args));
}
}
}
/** /**
* Spawn shell process with ksu.spawn * Spawn shell process with ksu.spawn
* @param {string} command - The command to execute * @param {string} command - The command to execute
@@ -71,20 +92,6 @@ export function spawn(command, args = [], options = {}) {
} }
} }
}; };
function Stdio() {
this.listeners = {};
}
Stdio.prototype.on = function(event, listener) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(listener);
};
Stdio.prototype.emit = function(event, ...args) {
if (this.listeners[event]) {
this.listeners[event].forEach(listener => listener(...args));
}
};
const callbackName = getUniqueCallbackName("spawn"); const callbackName = getUniqueCallbackName("spawn");
window[callbackName] = child; window[callbackName] = child;
child.on("exit", () => delete window[callbackName]); child.on("exit", () => delete window[callbackName]);

View File

@@ -22,14 +22,12 @@ document.getElementById("boot-hash").addEventListener("click", async () => {
}, 10); }, 10);
// read current boot hash // read current boot hash
exec("cat /data/adb/boot_hash") exec(`sed '/^#/d; /^$/d' /data/adb/boot_hash`)
.then(({ errno, stdout }) => { .then(({ errno, stdout }) => {
if (errno !== 0) { if (errno !== 0) {
inputBox.value = ""; inputBox.value = "";
} else { } else {
const validHash = stdout const validHash = stdout.trim();
.split("\n")
.filter(line => !line.startsWith("#") && line.trim())[0];
inputBox.value = validHash || ""; inputBox.value = validHash || "";
} }
}); });
@@ -48,7 +46,7 @@ const closeBootHashMenu = () => {
saveButton.addEventListener("click", async () => { saveButton.addEventListener("click", async () => {
const inputValue = inputBox.value.trim(); const inputValue = inputBox.value.trim();
exec(` exec(`
resetprop -n ro.boot.vbmeta.digest ${inputValue} resetprop -n ro.boot.vbmeta.digest "${inputValue}"
[ -z "${inputValue}" ] && rm -f /data/adb/boot_hash || { [ -z "${inputValue}" ] && rm -f /data/adb/boot_hash || {
echo "${inputValue}" > /data/adb/boot_hash echo "${inputValue}" > /data/adb/boot_hash
chmod 644 /data/adb/boot_hash chmod 644 /data/adb/boot_hash

View File

@@ -11,7 +11,7 @@ const title = document.querySelector('.header');
export const noConnection = document.querySelector('.no-connection'); export const noConnection = document.querySelector('.no-connection');
// Loading, Save and Prompt Elements // Loading, Save and Prompt Elements
const loadingIndicator = document.querySelector('.loading'); export const loadingIndicator = document.querySelector('.loading');
const prompt = document.getElementById('prompt'); const prompt = document.getElementById('prompt');
const floatingCard = document.querySelector('.floating-card'); const floatingCard = document.querySelector('.floating-card');
const floatingBtn = document.querySelector('.floating-btn'); const floatingBtn = document.querySelector('.floating-btn');
@@ -50,16 +50,12 @@ export async function refreshAppList() {
appListContainer.innerHTML = ''; appListContainer.innerHTML = '';
loadingIndicator.style.display = 'flex'; loadingIndicator.style.display = 'flex';
document.querySelector('.uninstall-container').classList.add('hidden-uninstall'); document.querySelector('.uninstall-container').classList.add('hidden-uninstall');
await new Promise(resolve => setTimeout(resolve, 500));
window.scrollTo(0, 0); window.scrollTo(0, 0);
if (noConnection.style.display === "flex") { if (noConnection.style.display === "flex") {
updateCheck(); updateCheck();
exec(`rm -f "${basePath}/common/tmp/exclude-list"`); exec(`rm -f "${basePath}/common/tmp/exclude-list"`);
} }
await fetchAppList(); fetchAppList();
applyRippleEffect();
loadingIndicator.style.display = 'none';
document.querySelector('.uninstall-container').classList.remove('hidden-uninstall');
isRefreshing = false; isRefreshing = false;
} }
@@ -201,7 +197,10 @@ function checkMMRL() {
// Funtion to adapt floating button hide in MMRL // Funtion to adapt floating button hide in MMRL
export function hideFloatingBtn(hide = true) { export function hideFloatingBtn(hide = true) {
if (!hide) floatingCard.style.transform = 'translateY(0)'; if (!hide) {
floatingCard.style.transform = 'translateY(0)';
floatingBtn.style.display = 'block';
}
else floatingCard.style.transform = 'translateY(calc(var(--window-inset-bottom, 0px) + 120px))'; else floatingCard.style.transform = 'translateY(calc(var(--window-inset-bottom, 0px) + 120px))';
} }
@@ -299,15 +298,10 @@ document.addEventListener('DOMContentLoaded', async () => {
setupMenuToggle(); setupMenuToggle();
setupLanguageMenu(); setupLanguageMenu();
setupSystemAppMenu(); setupSystemAppMenu();
await fetchAppList(); fetchAppList();
applyRippleEffect();
checkTrickyStoreVersion(); checkTrickyStoreVersion();
checkMagisk(); checkMagisk();
updateCheck(); updateCheck();
securityPatch(); securityPatch();
loadingIndicator.style.display = "none";
floatingBtn.style.display = 'block';
hideFloatingBtn(false);
document.getElementById("refresh").addEventListener("click", refreshAppList); document.getElementById("refresh").addEventListener("click", refreshAppList);
document.querySelector('.uninstall-container').classList.remove('hidden-uninstall');
}); });

View File

@@ -1,4 +1,4 @@
import { exec } from './assets/kernelsu.js'; import { exec, spawn } from './assets/kernelsu.js';
import { basePath, showPrompt } from './main.js'; import { basePath, showPrompt } from './main.js';
const overlay = document.getElementById('security-patch-overlay'); const overlay = document.getElementById('security-patch-overlay');
@@ -218,24 +218,29 @@ export function securityPatch() {
// Auto config button // Auto config button
autoButton.addEventListener('click', () => { autoButton.addEventListener('click', () => {
exec(`sh ${basePath}/common/get_extra.sh --security-patch`) const output = spawn('sh', [`${basePath}/common/get_extra.sh`, '--security-patch']);
.then(({ errno, stdout }) => { output.stdout.on('data', (data) => {
if (errno !== 0 || stdout.trim() === "not set") { if (data.includes("not set")) {
showPrompt('security_patch.auto_failed', false); showPrompt('security_patch.auto_failed', false);
} else { }
exec(`touch /data/adb/tricky_store/security_patch_auto_config`) });
// Reset inputs output.on('exit', (code) => {
allPatchInput.value = ''; if (code === 0) {
systemPatchInput.value = ''; exec(`touch /data/adb/tricky_store/security_patch_auto_config`)
bootPatchInput.value = ''; // Reset inputs
vendorPatchInput.value = ''; allPatchInput.value = '';
systemPatchInput.value = '';
bootPatchInput.value = '';
vendorPatchInput.value = '';
checkAdvanced(false); checkAdvanced(false);
showPrompt('security_patch.auto_success'); showPrompt('security_patch.auto_success');
} } else {
hideSecurityPatchDialog(); showPrompt('security_patch.auto_failed', false);
loadCurrentConfig(); }
}); hideSecurityPatchDialog();
loadCurrentConfig();
});
}); });
// Save button // Save button
@@ -308,21 +313,18 @@ export function securityPatch() {
// Get button // Get button
getButton.addEventListener('click', async () => { getButton.addEventListener('click', async () => {
showPrompt('security_patch.fetching'); showPrompt('security_patch.fetching');
setTimeout(() => { const output = spawn('sh', [`${basePath}/common/get_extra.sh`, '--get-security-patch']);
exec(`sh ${basePath}/common/get_extra.sh --get-security-patch`) output.stdout.on('data', (data) => {
.then(({ errno, stdout }) => { showPrompt('security_patch.fetched', true, 1000);
if (errno !== 0) { checkAdvanced(true);
showPrompt('security_patch.get_failed', false);
} else {
showPrompt('security_patch.fetched', true, 1000);
checkAdvanced(true);
allPatchInput.value = stdout.replace(/-/g, ''); allPatchInput.value = data.replace(/-/g, '');
systemPatchInput.value = 'prop'; systemPatchInput.value = 'prop';
bootPatchInput.value = stdout; bootPatchInput.value = data;
vendorPatchInput.value = stdout; vendorPatchInput.value = data;
} });
}) output.on('exit', (code) => {
}, 200); if (code !== 0) showPrompt('security_patch.get_failed', false);
});
}); });
} }

View File

@@ -1,4 +1,4 @@
import { exec } from './assets/kernelsu.js'; import { exec, spawn } from './assets/kernelsu.js';
import { basePath, showPrompt, noConnection, linkRedirect } from './main.js'; import { basePath, showPrompt, noConnection, linkRedirect } from './main.js';
import { updateCard } from './applist.js'; import { updateCard } from './applist.js';
@@ -52,12 +52,14 @@ export async function updateCheck() {
zipURL = data.zipUrl; zipURL = data.zipUrl;
changelogURL = data.changelog; changelogURL = data.changelog;
const { stdout } = await exec(`sh ${basePath}/common/get_extra.sh --check-update ${remoteVersionCode}`); const output = spawn('sh', [`${basePath}/common/get_extra.sh`, '--check-update', `${remoteVersionCode}`]);
if (stdout.includes("update")) { output.stdout.on('data', (data) => {
showPrompt("prompt.new_update", true, 1500); if (data.includes("update")) {
updateCard.style.display = "flex"; showPrompt("prompt.new_update", true, 1500);
setupUpdateMenu(); updateCard.style.display = "flex";
} setupUpdateMenu();
}
});
} catch (error) { } catch (error) {
console.error("Error fetching JSON or executing command:", error); console.error("Error fetching JSON or executing command:", error);
showPrompt("prompt.no_internet", false); showPrompt("prompt.no_internet", false);
@@ -111,43 +113,41 @@ function setupUpdateMenu() {
// Update card // Update card
updateCard.addEventListener('click', async () => { updateCard.addEventListener('click', async () => {
try { const { stdout } = await exec(`
const { stdout } = await exec(` [ -f ${basePath}/common/tmp/module.zip ] || echo "noModule"
[ -f ${basePath}/common/tmp/module.zip ] || echo "noModule" [ -f ${basePath}/common/tmp/changelog.md ] || echo "noChangelog"
[ -f ${basePath}/common/tmp/changelog.md ] || echo "noChangelog" [ ! -f /data/adb/modules/TA_utl/update ] || echo "updated"
[ ! -f /data/adb/modules/TA_utl/update ] || echo "updated" `);
`); if (stdout.trim().includes("updated")) {
if (stdout.trim().includes("updated")) { installButton.style.display = "none";
installButton.style.display = "none"; rebootButton.style.display = "flex";
rebootButton.style.display = "flex"; openUpdateMenu();
openUpdateMenu(); } else if (stdout.trim().includes("noChangelog")) {
} else if (stdout.trim().includes("noChangelog")) { showPrompt("prompt.downloading");
showPrompt("prompt.downloading"); await downloadFile(changelogURL, "changelog.md");
await downloadFile(changelogURL, "changelog.md"); renderChangelog();
renderChangelog(); openUpdateMenu();
openUpdateMenu(); setTimeout(() => {
setTimeout(() => { updateCard.click();
updateCard.click(); }, 200);
}, 200); } else if (stdout.trim().includes("noModule")) {
} else if (stdout.trim().includes("noModule")) { if (downloading) return;
if (downloading) return; downloading = true;
downloading = true; const download = spawn('sh', [`${basePath}/common/get_extra.sh`, '--get-update', `${zipURL}`],
const { errno } = await exec(`sh ${basePath}/common/get_extra.sh --get-update ${zipURL}`); { env: { PATH: "$PATH:/data/adb/ap/bin:/data/adb/ksu/bin:/data/adb/magisk:/data/data/com.termux/files/usr/bin" } });
if (errno === 0) { download.on('exit', (code) => {
downloading = false;
if (code === 0) {
showPrompt("prompt.downloaded"); showPrompt("prompt.downloaded");
installButton.style.display = "flex"; installButton.style.display = "flex";
} else { } else {
showPrompt("prompt.download_fail", false); showPrompt("prompt.download_fail", false);
} }
downloading = false; });
} else { } else {
installButton.style.display = "flex"; installButton.style.display = "flex";
renderChangelog(); renderChangelog();
openUpdateMenu(); openUpdateMenu();
}
} catch (error) {
showPrompt("prompt.download_fail", false);
console.error('Error download module update:', error);
} }
}); });
@@ -160,16 +160,20 @@ function setupUpdateMenu() {
// Install button // Install button
installButton.addEventListener('click', async () => { installButton.addEventListener('click', async () => {
showPrompt("prompt.installing"); showPrompt("prompt.installing");
await new Promise(resolve => setTimeout(resolve, 300)); const output = spawn('sh', [`${basePath}/common/get_extra.sh`, '--install-update'],
const { errno, stderr } = await exec(`sh ${basePath}/common/get_extra.sh --install-update`); { env: { PATH: "$PATH:/data/adb/ap/bin:/data/adb/ksu/bin:/data/adb/magisk" } });
if (errno === 0) { output.stderr.on('data', (data) => {
showPrompt("prompt.installed"); console.error('Error during installation:', data);
installButton.style.display = "none"; })
rebootButton.style.display = "flex"; output.on('exit', (code) => {
} else { if (code === 0) {
showPrompt("prompt.install_fail", false); showPrompt("prompt.installed");
console.error('Fail to execute installation script:', stderr); installButton.style.display = "none";
} rebootButton.style.display = "flex";
} else {
showPrompt("prompt.install_fail", false);
}
});
}); });
// Reboot button // Reboot button

View File

@@ -58,12 +58,16 @@
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
} }
.update-content h1,
.update-content h3 { .update-content h3 {
font-size: 24px;
margin: 10px 0; margin: 10px 0;
margin-top: 0; margin-top: 0;
} }
.update-content h3 {
font-size: 24px;
}
.changelog { .changelog {
max-height: 65vh; max-height: 65vh;
overflow-y: auto; overflow-y: auto;