Compare commits

...

9 Commits

Author SHA1 Message Date
Devin Bot
998e16674c updated git ignore 2026-10-25 20:11:43 -07:00
Devin Bot
179372dad3 Pin Tauri packages to matching versions 2026-10-25 20:11:32 -07:00
Devin Bot
59d25f53dd v1.1.48 2026-10-25 19:53:06 -07:00
Devin Bot
4ed853b5ae v1.1.47 2026-10-25 19:32:10 -07:00
Devin Bot
1b2c97e74b potentially fixed auto updater, made sdk also up to date 2026-10-25 19:27:25 -07:00
Devin Bot
d9ae75e9cb v1.1.46 2026-10-24 18:32:54 -07:00
Devin Bot
51951ac192 removed floating nav, made the updater better...? 2026-10-24 18:28:09 -07:00
Devin Bot
c131515209 added white icon 2026-10-24 18:13:14 -07:00
Devin Bot
b44f59f4b7 v1.1.45 2026-10-23 20:47:05 -07:00
13 changed files with 1694 additions and 241 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
node_modules/ node_modules/
package-lock.json
dist/ dist/
dist-build/ dist-build/

BIN
build/white_favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

View File

@@ -21,7 +21,7 @@ workflows:
- name: Install dependencies - name: Install dependencies
script: | script: |
export PATH="$HOME/.cargo/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH"
npm install npm ci
- name: Build & Publish macOS Release - name: Build & Publish macOS Release
script: | script: |
export PATH="$HOME/.cargo/bin:$PATH" export PATH="$HOME/.cargo/bin:$PATH"

92
main.js
View File

@@ -179,20 +179,37 @@ async function start() {
debugLog('Download complete. Verifying checksum...'); debugLog('Download complete. Verifying checksum...');
sendToTauri('status', { message: 'Verifying SHA-256 integrity...' }); sendToTauri('status', { message: 'Verifying SHA-256 integrity...' });
if (!release.sha256) {
throw new Error('Release has no SHA-256 in manifest.json; refusing unverified install.');
}
if (!patcher.verifyChecksum(tempZipPath, release.sha256)) { if (!patcher.verifyChecksum(tempZipPath, release.sha256)) {
throw new Error('SHA-256 checksum verification failed.'); throw new Error('SHA-256 checksum verification failed.');
} }
debugLog('Checksum verified. Extracting...'); debugLog('Checksum verified. Extracting to staging...');
sendToTauri('status', { message: 'Unpacking update archive...' }); sendToTauri('status', { message: 'Unpacking update archive...' });
patcher.purgeOldCode(); let lastEmit = 0;
patcher.extractZipPayload(tempZipPath); const stagingDir = await patcher.extractToStaging(tempZipPath, (file, done, total) => {
const now = Date.now();
if (done !== total && now - lastEmit < 40) return;
lastEmit = now;
sendToTauri('extract-progress', {
file,
done,
total,
percent: Math.round((done / total) * 100)
});
});
sendToTauri('status', { message: 'Verifying file integrity...' }); sendToTauri('status', { message: 'Verifying file integrity...' });
if (!patcher.verifyExtraction()) { if (!patcher.verifyExtraction(stagingDir)) {
fs.rmSync(stagingDir, { recursive: true, force: true });
throw new Error('Extraction verification failed: critical startup files missing.'); throw new Error('Extraction verification failed: critical startup files missing.');
} }
sendToTauri('status', { message: 'Replacing old version...' });
patcher.commitStaging(stagingDir);
debugLog('Extraction complete. Applying patches...'); debugLog('Extraction complete. Applying patches...');
sendToTauri('status', { message: 'Applying configuration patches...' }); sendToTauri('status', { message: 'Applying configuration patches...' });
patcher.applyPatches(getReplacementsConfig()); patcher.applyPatches(getReplacementsConfig());
@@ -224,9 +241,9 @@ async function start() {
try { try {
sendToTauri('status', { message: 'Inspecting local workspace and installed files...' }); sendToTauri('status', { message: 'Inspecting local workspace and installed files...' });
let isFirstRun = !patcher.verifyExtraction(); const isFirstRun = !patcher.verifyExtraction();
let localVersion = patcher.getCurrentVersion(); const localVersion = patcher.getCurrentVersion();
let localSha = patcher.getCurrentSha(); const localSha = patcher.getCurrentSha();
debugLog(`Is First Run: ${isFirstRun}, Local Version: ${localVersion}, Local SHA: ${localSha}`); debugLog(`Is First Run: ${isFirstRun}, Local Version: ${localVersion}, Local SHA: ${localSha}`);
function findLocalZip() { function findLocalZip() {
@@ -249,36 +266,10 @@ async function start() {
return null; return null;
} }
const localFound = findLocalZip(); let localZipPath = null;
let localZipPath = localFound ? localFound.zipPath : null; let localManifestPath = null;
let localManifestPath = localFound ? localFound.manifestPath : null;
let localZipManifest = null; let localZipManifest = null;
if (localManifestPath) {
try {
localZipManifest = JSON.parse(fs.readFileSync(localManifestPath, 'utf8'));
} catch (e) { }
}
if (localZipPath && (isFirstRun || (localZipManifest && localZipManifest.sha256 !== localSha))) {
debugLog(`Applying local development payload from: ${localZipPath}`);
sendToTauri('status', { message: 'Extracting local development payload...' });
patcher.purgeOldCode();
patcher.extractZipPayload(localZipPath);
patcher.applyPatches(getReplacementsConfig());
patcher.bridgeCredentials(serviceManager.credentialManager);
const ver = localZipManifest ? localZipManifest.version : '1.1.32';
const sha = localZipManifest ? localZipManifest.sha256 : null;
patcher.saveCurrentVersion(ver, sha);
serviceManager.envConfig = getEnvConfig();
isFirstRun = false;
localVersion = ver;
localSha = sha;
debugLog("Local payload applied successfully.");
}
sendToTauri('status', { message: 'Connecting to update servers...' }); sendToTauri('status', { message: 'Connecting to update servers...' });
let release = null; let release = null;
try { try {
@@ -292,17 +283,6 @@ async function start() {
} catch (e) { } } catch (e) { }
if (release && release.version) { if (release && release.version) {
const rVer = release.version.replace('v', '').split('.').map(Number);
const aVer = appPkgVer.split('.').map(Number);
let isOlder = false;
for (let i = 0; i < 3; i++) {
if ((rVer[i] || 0) < (aVer[i] || 0)) { isOlder = true; break; }
if ((rVer[i] || 0) > (aVer[i] || 0)) { break; }
}
if (isOlder) {
debugLog(`Remote version ${release.version} is older than local package ${appPkgVer}. Ignoring remote release.`);
release = null;
}
} }
} catch (e) { } catch (e) {
debugLog(`Update check failed (offline?): ${e.message}`); debugLog(`Update check failed (offline?): ${e.message}`);
@@ -310,11 +290,23 @@ async function start() {
} }
const normalizeVer = (v) => (v || '').toString().replace(/^v/, '').trim(); const normalizeVer = (v) => (v || '').toString().replace(/^v/, '').trim();
const parseVer = (v) => normalizeVer(v).split(/[.\-+]/).map((n) => parseInt(n, 10) || 0);
const isNewer = (remote, local) => {
const a = parseVer(remote);
const b = parseVer(local);
for (let i = 0; i < Math.max(a.length, b.length); i++) {
const x = a[i] || 0;
const y = b[i] || 0;
if (x !== y) return x > y;
}
return false;
};
if (release) { if (release) {
const isNewerSha = release.sha256 && localSha && release.sha256 !== localSha; const versionNewer = isNewer(release.version, localVersion);
const isDifferentVer = normalizeVer(release.version) !== normalizeVer(localVersion); const sameVerNewBuild = normalizeVer(release.version) === normalizeVer(localVersion) &&
const hasUpdate = (isDifferentVer || isNewerSha) && !localZipPath; !!release.sha256 && !!localSha && release.sha256 !== localSha;
const hasUpdate = (versionNewer || sameVerNewBuild) && !localZipPath;
if (isFirstRun) { if (isFirstRun) {
debugLog("First run with no local payload. Installing remote build..."); debugLog("First run with no local payload. Installing remote build...");
@@ -323,7 +315,7 @@ async function start() {
} else if (hasUpdate) { } else if (hasUpdate) {
debugLog("Update available. Prompting user..."); debugLog("Update available. Prompting user...");
sendToTauri('status', { message: `New update available (${release.version}). Awaiting confirmation...` }); sendToTauri('status', { message: `New update available (${release.version}).` });
try { try {
if (process.platform === 'win32') { if (process.platform === 'win32') {

1534
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "vyla-home", "name": "vyla-home",
"version": "1.1.43", "version": "1.1.48",
"description": "Vyla Home Desktop App Launcher", "description": "Vyla Home Desktop App Launcher",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
@@ -11,16 +11,16 @@
"release:full": "node release.js --installer" "release:full": "node release.js --installer"
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.12.1", "@tauri-apps/api": "2.12.1",
"@tauri-apps/plugin-opener": "^2.7.0", "@tauri-apps/plugin-opener": "2.7.0",
"@tauri-apps/plugin-process": "^2.4.0", "@tauri-apps/plugin-process": "2.4.0",
"@tauri-apps/plugin-shell": "^2.4.0", "@tauri-apps/plugin-shell": "2.5.0",
"@vyla-entertainment/sdk": "^1.18.0", "@vyla-entertainment/sdk": "^1.20.0",
"adm-zip": "^0.6.0", "adm-zip": "^0.6.0",
"express": "^4.19.2" "express": "^4.19.2"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^2.0.0" "@tauri-apps/cli": "2.12.1"
}, },
"build": { "build": {
"appId": "com.vyla.home", "appId": "com.vyla.home",

View File

@@ -105,23 +105,51 @@ class Patcher {
} }
} }
async extractToStaging(zipPath, onFile) {
const stagingDir = path.join(this.appDataPath, '.staging');
fs.rmSync(stagingDir, { recursive: true, force: true });
fs.mkdirSync(stagingDir, { recursive: true });
const zip = new AdmZip(zipPath);
const entries = zip.getEntries().filter((e) => !e.isDirectory);
if (entries.length === 0) {
throw new Error('Downloaded archive is empty, invalid, or truncated.');
}
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
zip.extractEntryTo(entry, stagingDir, true, true);
if (onFile) onFile(entry.entryName, i + 1, entries.length);
if (i % 20 === 0) await new Promise((resolve) => setImmediate(resolve));
}
return stagingDir;
}
commitStaging(stagingDir) {
this.purgeOldCode();
for (const name of fs.readdirSync(stagingDir)) {
const dest = path.join(this.appDataPath, name);
fs.rmSync(dest, { recursive: true, force: true });
fs.renameSync(path.join(stagingDir, name), dest);
}
fs.rmSync(stagingDir, { recursive: true, force: true });
}
verifyChecksum(zipPath, expectedSha256) { verifyChecksum(zipPath, expectedSha256) {
if (!expectedSha256) return true; if (!expectedSha256) return false;
const hash = crypto.createHash('sha256'); const hash = crypto.createHash('sha256');
const data = fs.readFileSync(zipPath); const data = fs.readFileSync(zipPath);
hash.update(data); hash.update(data);
const calculated = hash.digest('hex'); const calculated = hash.digest('hex');
return calculated === expectedSha256; return calculated === String(expectedSha256).trim().toLowerCase();
} }
verifyExtraction() { verifyExtraction(root = this.appDataPath) {
const requiredFiles = [ const requiredFiles = [
'frontend/server.js', 'frontend/server.js',
'player/server.js' 'player/server.js'
]; ];
for (const file of requiredFiles) { for (const file of requiredFiles) {
const fullPath = path.join(this.appDataPath, file); const fullPath = path.join(root, file);
if (!fs.existsSync(fullPath)) { if (!fs.existsSync(fullPath)) {
return false; return false;
} }

52
src-tauri/Cargo.lock generated
View File

@@ -741,7 +741,7 @@ dependencies = [
"libc", "libc",
"option-ext", "option-ext",
"redox_users", "redox_users",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -921,7 +921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -2062,7 +2062,7 @@ dependencies = [
"png 0.18.1", "png 0.18.1",
"serde", "serde",
"thiserror 2.0.20", "thiserror 2.0.20",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -2390,7 +2390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -2844,7 +2844,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -3125,7 +3125,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7"
dependencies = [ dependencies = [
"libc", "libc",
"sigchld",
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]
@@ -3135,27 +3134,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "sigchld"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1"
dependencies = [
"libc",
"os_pipe",
"signal-hook",
]
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.8" version = "1.4.8"
@@ -3197,7 +3175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -3580,11 +3558,12 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-shell" name = "tauri-plugin-shell"
version = "2.4.0" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "094bdbc47e5efe36544ba21b3a53ab5c724150236cf55e6c9be46191deb6eab6" checksum = "8567d42b2c7264355daa1df415ec77f52aa42e885924c86a969be2e920e05521"
dependencies = [ dependencies = [
"encoding_rs", "encoding_rs",
"libc",
"log", "log",
"open", "open",
"os_pipe", "os_pipe",
@@ -3597,6 +3576,7 @@ dependencies = [
"tauri-plugin", "tauri-plugin",
"thiserror 2.0.20", "thiserror 2.0.20",
"tokio", "tokio",
"windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -3709,7 +3689,7 @@ dependencies = [
"getrandom 0.4.3", "getrandom 0.4.3",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -4034,7 +4014,7 @@ dependencies = [
"png 0.18.1", "png 0.18.1",
"serde", "serde",
"thiserror 2.0.20", "thiserror 2.0.20",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -4063,7 +4043,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [ dependencies = [
"memoffset", "memoffset",
"tempfile", "tempfile",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -4155,7 +4135,7 @@ dependencies = [
[[package]] [[package]]
name = "vyla-home" name = "vyla-home"
version = "1.1.43" version = "1.1.48"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"objc2 0.5.2", "objc2 0.5.2",
@@ -4402,7 +4382,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -4423,7 +4403,7 @@ dependencies = [
"objc2-foundation", "objc2-foundation",
"objc2-quartz-core", "objc2-quartz-core",
"raw-window-handle", "raw-window-handle",
"windows-sys 0.60.2", "windows-sys 0.61.2",
"windows-version", "windows-version",
] ]

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "vyla-home" name = "vyla-home"
version = "1.1.43" version = "1.1.48"
description = "Vyla Home Desktop App Launcher" description = "Vyla Home Desktop App Launcher"
edition = "2021" edition = "2021"
@@ -11,7 +11,7 @@ tauri-build = { version = "2", features = [] }
tauri = { version = "2", features = ["devtools"] } tauri = { version = "2", features = ["devtools"] }
tauri-plugin-opener = "2" tauri-plugin-opener = "2"
tauri-plugin-process = "2" tauri-plugin-process = "2"
tauri-plugin-shell = "2" tauri-plugin-shell = "2.5"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
base64 = "0.22" base64 = "0.22"

View File

@@ -269,110 +269,11 @@ const LINK_HANDLER_SCRIPT: &str =
}, true); }, true);
function injectLiquidNavBar() { function injectLiquidNavBar() {
if (document.getElementById('vyla-floating-nav')) return; if (document.getElementById('tauri-custom-titlebar')) return;
if (!document.body) return; if (!document.body) return;
const style = document.createElement('style'); const style = document.createElement('style');
style.innerHTML = ` style.innerHTML = `
#vyla-floating-nav {
position: fixed !important;
top: 12px !important;
left: 12px !important;
z-index: 2147483647 !important;
height: 42px !important;
width: 42px !important;
border-radius: 9999px !important;
display: flex !important;
align-items: center !important;
background: rgba(255, 255, 255, 0.04) !important;
backdrop-filter: blur(16px) saturate(1.8) contrast(1.1) !important;
-webkit-backdrop-filter: blur(16px) saturate(1.8) contrast(1.1) !important;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45) !important;
transition: width 0.3s cubic-bezier(0.16, 1, 0.3, 1), padding 0.3s cubic-bezier(0.16, 1, 0.3, 1) !important;
overflow: hidden !important;
padding: 0 !important;
user-select: none !important;
pointer-events: auto !important;
-webkit-app-region: no-drag !important;
}
#vyla-floating-nav:hover {
width: 82px !important;
padding-right: 6px !important;
}
.vyla-nav-logo-wrap {
width: 42px !important;
min-width: 42px !important;
height: 42px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
cursor: pointer !important;
border-radius: 9999px !important;
position: relative !important;
z-index: 2147483647 !important;
pointer-events: auto !important;
padding: 0 !important;
}
.vyla-nav-logo-wrap img {
width: 26px !important;
height: 26px !important;
min-width: 26px !important;
min-height: 26px !important;
object-fit: contain !important;
pointer-events: none !important;
display: block !important;
}
.vyla-nav-links {
display: flex !important;
align-items: center !important;
gap: 4px !important;
opacity: 0 !important;
transform: translateX(-6px) !important;
transition: opacity 0.2s ease, transform 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important;
pointer-events: none !important;
position: relative !important;
z-index: 2 !important;
margin-left: 2px !important;
}
#vyla-floating-nav:hover .vyla-nav-links {
opacity: 1 !important;
transform: translateX(0) !important;
pointer-events: auto !important;
}
.vyla-nav-btn {
width: 32px !important;
min-width: 32px !important;
height: 32px !important;
border-radius: 9999px !important;
border: none !important;
outline: none !important;
background: transparent !important;
box-shadow: none !important;
color: #ffffff !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
cursor: pointer !important;
transition: transform 0.15s ease, opacity 0.2s ease !important;
opacity: 0.85 !important;
padding: 0 !important;
}
.vyla-nav-btn:hover { opacity: 1 !important; }
.vyla-nav-btn svg {
width: 20px !important;
height: 20px !important;
fill: #ffffff !important;
pointer-events: none !important;
display: block !important;
}
#tauri-custom-titlebar { #tauri-custom-titlebar {
position: fixed !important; position: fixed !important;
top: 0 !important; top: 0 !important;
@@ -429,38 +330,6 @@ const LINK_HANDLER_SCRIPT: &str =
`; `;
document.head.appendChild(style); document.head.appendChild(style);
const nav = document.createElement('div');
nav.id = 'vyla-floating-nav';
nav.innerHTML = `
<div class="vyla-nav-logo-wrap" title="Go to Home">
<img src="https://gitlab.com/vyla-entertainment/go/-/raw/main/assets/logos/white_logo.png" alt="Logo" />
</div>
<div class="vyla-nav-links">
<button class="vyla-nav-btn" id="vyla-btn-discord" title="Discord Community">
<svg viewBox="0 0 127.14 96.36"><path d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,45.91,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,45.91,96.12,53,91.08,65.69,84.69,65.69Z"/></svg>
</button>
</div>
`;
document.body.appendChild(nav);
const logoBtn = nav.querySelector('.vyla-nav-logo-wrap');
if (logoBtn) {
logoBtn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
window.location.href = 'http://127.0.0.1:4890';
};
}
const btnDiscord = nav.querySelector('#vyla-btn-discord');
if (btnDiscord) {
btnDiscord.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
openExternalUrl('https://discord.gg/5eqRhKeywG');
};
}
const tb = document.createElement('div'); const tb = document.createElement('div');
tb.id = 'tauri-custom-titlebar'; tb.id = 'tauri-custom-titlebar';
tb.innerHTML = ` tb.innerHTML = `

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Vyla Home", "productName": "Vyla Home",
"version": "1.1.43", "version": "1.1.48",
"identifier": "com.vyla.home", "identifier": "com.vyla.home",
"build": { "build": {
"frontendDist": "../ui" "frontendDist": "../ui"

View File

@@ -87,6 +87,21 @@
} }
} }
.file-line {
width: 100%;
min-height: 16px;
margin-top: -12px;
margin-bottom: 14px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 11px;
color: rgba(255, 255, 255, 0.45);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
direction: rtl;
text-align: center;
}
.progress-track { .progress-track {
width: 100%; width: 100%;
height: 10px; height: 10px;
@@ -196,8 +211,9 @@
<div class="loader-card"> <div class="loader-card">
<div class="status-text" id="status-message">Starting runtime engine...</div> <div class="status-text" id="status-message">Starting runtime engine...</div>
<div id="debug-log-box" <div id="debug-log-box"
style="display: none; width: 100%; max-height: 180px; overflow-y: auto; background: rgba(255, 50, 50, 0.12); border: 1px solid rgba(255, 100, 100, 0.3); border-radius: 8px; padding: 12px; font-family: monospace; font-size: 12px; color: #fca5a5; text-align: left; white-space: pre-wrap; word-break: break-all; margin-bottom: 20px; user-select: text;"> style="display: none; width: 100%; max-height: 180px; overflow-y: auto; background: rgba(255, 50, 50, 0.12); border-radius: 15px; padding: 12px; font-family: monospace; font-size: 12px; color: #fca5a5; text-align: left; white-space: pre-wrap; word-break: break-all; margin-bottom: 20px; user-select: text;">
</div> </div>
<div class="file-line" id="file-line"></div>
<div class="progress-track" id="progress-track" style="display: none;"> <div class="progress-track" id="progress-track" style="display: none;">
<div class="progress-fill" id="progress-fill"></div> <div class="progress-fill" id="progress-fill"></div>
</div> </div>
@@ -515,6 +531,8 @@ void main() {
const actionsEl = document.getElementById("update-actions"); const actionsEl = document.getElementById("update-actions");
const btnYes = document.getElementById("btn-update-yes"); const btnYes = document.getElementById("btn-update-yes");
const btnNo = document.getElementById("btn-update-no"); const btnNo = document.getElementById("btn-update-no");
const fileLine = document.getElementById("file-line");
let currentService = "";
if (actionsEl) actionsEl.style.display = "none"; if (actionsEl) actionsEl.style.display = "none";
if (progressTrack) progressTrack.style.display = "none"; if (progressTrack) progressTrack.style.display = "none";
@@ -562,17 +580,37 @@ void main() {
eventApi.listen("status", (e) => { eventApi.listen("status", (e) => {
const data = e.payload || e; const data = e.payload || e;
if (data && data.message) { if (data && data.message) {
if (fileLine) fileLine.textContent = "";
currentService = "";
setStatus(data.message); setStatus(data.message);
} }
}); });
eventApi.listen("extract-progress", (e) => {
const data = e.payload || e;
if (!data || !data.file) return;
if (progressTrack) progressTrack.style.display = "block";
if (actionsEl) actionsEl.style.display = "none";
if (progressFill) progressFill.style.width = `${data.percent}%`;
const parts = String(data.file).split("/");
const service = parts.length > 1 ? parts[0] : "core files";
if (service !== currentService) {
currentService = service;
setStatus(`Installing ${service}...`);
}
if (fileLine) fileLine.textContent = `${data.file} (${data.done}/${data.total})`;
});
eventApi.listen("download-progress", (e) => { eventApi.listen("download-progress", (e) => {
const data = e.payload || e; const data = e.payload || e;
if (data) { if (data) {
if (progressTrack) progressTrack.style.display = "block"; if (progressTrack) progressTrack.style.display = "block";
if (progressFill) progressFill.style.width = "100%"; if (progressFill && data.percent !== undefined) {
progressFill.style.width = `${data.percent}%`;
}
if (actionsEl) actionsEl.style.display = "none"; if (actionsEl) actionsEl.style.display = "none";
setStatus("Downloading update..."); setStatus(`Downloading update... ${data.percent !== undefined ? data.percent + '%' : ''}`);
} }
}); });
@@ -590,6 +628,7 @@ void main() {
if (data && data.url) { if (data && data.url) {
if (progressTrack) progressTrack.style.display = "none"; if (progressTrack) progressTrack.style.display = "none";
if (actionsEl) actionsEl.style.display = "none"; if (actionsEl) actionsEl.style.display = "none";
if (fileLine) fileLine.textContent = "";
setStatus("Launching..."); setStatus("Launching...");
} }
}); });
@@ -597,6 +636,7 @@ void main() {
eventApi.listen("error", (e) => { eventApi.listen("error", (e) => {
const data = e.payload || e; const data = e.payload || e;
const msg = data && data.message ? data.message : JSON.stringify(data); const msg = data && data.message ? data.message : JSON.stringify(data);
if (fileLine) fileLine.textContent = "";
setStatus("Runtime Error", "#fca5a5"); setStatus("Runtime Error", "#fca5a5");
showDebug(msg); showDebug(msg);
}); });

View File

@@ -36,7 +36,9 @@ class UpdateClient {
const urlObj = new URL(url); const urlObj = new URL(url);
const headers = { const headers = {
'User-Agent': 'VylaHome-Client', 'User-Agent': 'VylaHome-Client',
'Accept': 'application/json' 'Accept': 'application/json',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
}; };
if (this.token) { if (this.token) {
@@ -235,12 +237,21 @@ class UpdateClient {
} }
async checkForUpdates() { async checkForUpdates() {
const cacheBuster = `?cb=${Date.now()}`; const query = `?per_page=20&order_by=released_at&sort=desc&cb=${Date.now()}`;
const releases = await this.fetchJson(RELEASES_API + cacheBuster); const releases = await this.fetchJson(RELEASES_API + query);
if (!releases || releases.length === 0) { if (!releases || releases.length === 0) {
return null; return null;
} }
const release = releases[0]; const verParts = (v) => String(v || '').replace(/^v/, '').split(/[.\-+]/).map((n) => parseInt(n, 10) || 0);
const cmp = (a, b) => {
const x = verParts(a);
const y = verParts(b);
for (let i = 0; i < Math.max(x.length, y.length); i++) {
if ((x[i] || 0) !== (y[i] || 0)) return (x[i] || 0) - (y[i] || 0);
}
return 0;
};
const release = releases.slice().sort((a, b) => cmp(b.tag_name, a.tag_name))[0];
const payloadAsset = (release.assets && release.assets.links ? release.assets.links : []).find(a => a.name === 'update-payload.zip'); const payloadAsset = (release.assets && release.assets.links ? release.assets.links : []).find(a => a.name === 'update-payload.zip');
const manifestAsset = (release.assets && release.assets.links ? release.assets.links : []).find(a => a.name === 'manifest.json'); const manifestAsset = (release.assets && release.assets.links ? release.assets.links : []).find(a => a.name === 'manifest.json');