diff --git a/main.js b/main.js index 5959fd5..2493d97 100644 --- a/main.js +++ b/main.js @@ -113,55 +113,6 @@ function portInUse(port) { }); } -function promptUser(event, payload, timeoutMs = 120000) { - return new Promise((resolve) => { - const token = require('crypto').randomBytes(24).toString('hex'); - const app = express(); - let server = null; - let finished = false; - let timer = null; - - const finish = (result) => { - if (finished) return; - finished = true; - clearTimeout(timer); - if (server) server.close(); - resolve(result); - }; - - timer = setTimeout(() => finish(null), timeoutMs); - - app.use((req, res, next) => { - const ip = req.socket.remoteAddress; - if (ip !== '127.0.0.1' && ip !== '::1' && ip !== '::ffff:127.0.0.1') { - return res.status(403).end(); - } - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Methods', 'POST,OPTIONS'); - res.header('Access-Control-Allow-Headers', 'Content-Type'); - if (req.method === 'OPTIONS') return res.sendStatus(200); - next(); - }); - - app.post('/prompt/answer', express.json({ limit: '64kb' }), (req, res) => { - const body = req.body || {}; - if (body.token !== token) return res.status(403).end(); - res.sendStatus(200); - finish({ - accept: body.accept === true, - files: Array.isArray(body.files) ? body.files.map(String) : [] - }); - }); - - server = app.listen(4891, '127.0.0.1', () => { - sendToTauri(event, { ...payload, token }); - }); - server.on('error', (err) => { - debugLog(`Prompt server bind error: ${err.message}`); - finish(null); - }); - }); -} async function start() { debugLog("Inside start() function"); @@ -298,79 +249,9 @@ async function start() { sendToTauri('status', { message: 'First run detected. Preparing initial installation...' }); await installBuild(release); } else if (hasUpdate) { - debugLog("Update available. Prompting user..."); - sendToTauri('status', { message: `New update available (${release.version}).` }); - - const tempApp = express(); - let userDecision = null; - updateDecisionHandler = (val) => { userDecision = val; }; - - tempApp.use((req, res, next) => { - const clientIp = req.socket.remoteAddress; - if (clientIp !== '127.0.0.1' && clientIp !== '::1' && clientIp !== '::ffff:127.0.0.1') { - return res.status(403).end(); - } - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS'); - res.header('Access-Control-Allow-Headers', '*'); - if (req.method === 'OPTIONS') return res.sendStatus(200); - next(); - }); - - tempApp.get('/update/info', (req, res) => { - res.json({ available: true, current: localVersion, latest: release.version }); - }); - const handleYes = (req, res) => { debugLog("tempApp received YES"); userDecision = true; res.sendStatus(200); }; - const handleNo = (req, res) => { debugLog("tempApp received NO"); userDecision = false; res.sendStatus(200); }; - - tempApp.all('/update/yes', handleYes); - tempApp.all('/update/install', handleYes); - tempApp.all('/update/no', handleNo); - tempApp.all('/update/skip', handleNo); - tempApp.all('/update/:choice', (req, res) => { - const c = String(req.params.choice || '').toLowerCase(); - if (c === 'yes' || c === 'install' || c === 'true') userDecision = true; - if (c === 'no' || c === 'skip' || c === 'false') userDecision = false; - res.sendStatus(200); - }); - - const tempServer = await new Promise((resolve) => { - const s = tempApp.listen(4891, '127.0.0.1', () => resolve(s)); - s.on('error', (err) => { - debugLog(`Update server bind error: ${err.message}`); - resolve(null); - }); - }); - - if (!tempServer) { - userDecision = false; - } else { - sendToTauri('update-available', { current: localVersion, latest: release.version }); - } - - let waitLoops = 0; - while (userDecision === null) { - if (waitLoops > 24000) { - debugLog("Update decision timed out after 10 minutes, defaulting to skip."); - userDecision = false; - break; - } - waitLoops++; - await new Promise(r => setTimeout(r, 25)); - } - - updateDecisionHandler = null; - if (tempServer) { - tempServer.close(); - } - - if (userDecision) { - debugLog("User accepted update."); - await installBuild(release); - } else { - debugLog("User skipped update."); - sendToTauri('status', { message: 'Update skipped. Continuing startup...' }); - } + debugLog("Update available. Installing update..."); + sendToTauri('status', { message: `Updating to ${release.version}...` }); + await installBuild(release); } else { debugLog("Already on the latest version."); sendToTauri('status', { message: 'Up to date.' }); @@ -406,32 +287,6 @@ async function start() { throw new Error('Could not download the app files. Check your internet connection and relaunch.'); } - if (process.env.VYLA_TEST_UI === '1') { - const fakeFiles = [ - { path: 'frontend/server.js', status: 'modified', size: 1024 }, - { path: 'frontend/public/app.js', status: 'modified', size: 2048 }, - { path: 'player/server.js', status: 'modified', size: 512 }, - { path: 'player/lib/new-file.js', status: 'added', size: 256 } - ]; - const filesAnswer = await promptUser('files-available', { files: fakeFiles }); - debugLog(`TEST files answer: ${JSON.stringify(filesAnswer)}`); - if (filesAnswer && filesAnswer.accept) { - for (let i = 1; i <= filesAnswer.files.length; i++) { - sendToTauri('extract-progress', { - file: filesAnswer.files[i - 1], - done: i, - total: filesAnswer.files.length, - percent: Math.round((i / filesAnswer.files.length) * 100) - }); - await new Promise((r) => setTimeout(r, 400)); - } - } - const sdkAnswer = await promptUser('sdk-available', { current: '1.19.0', latest: '1.20.0' }); - debugLog(`TEST sdk answer: ${JSON.stringify(sdkAnswer)}`); - sendToTauri('status', { message: sdkAnswer && sdkAnswer.accept ? 'TEST: SDK accepted' : 'TEST: SDK skipped' }); - await new Promise((r) => setTimeout(r, 1500)); - } - sendToTauri('status', { message: 'Checking UI files and SDK...' }); const [filesCheck, sdkCheck] = await Promise.all([ withTimeout(fileUpdater.checkFiles().catch((e) => { @@ -445,39 +300,26 @@ async function start() { ]); if (filesCheck && filesCheck.changes.length > 0) { - sendToTauri('status', { message: `${filesCheck.changes.length} file update(s) available.` }); - const answer = await promptUser('files-available', { - files: filesCheck.changes.map((c) => ({ path: c.path, status: c.status, size: c.size })) - }); - if (answer && answer.accept) { - const allowed = new Set(answer.files); - const selected = filesCheck.changes.filter((c) => allowed.has(c.path)); - if (selected.length > 0) { - try { - await fileUpdater.applyFiles(selected, filesCheck.seq, getReplacementsConfig(), (file, done, total) => { - sendToTauri('extract-progress', { file, done, total, percent: Math.round((done / total) * 100) }); - }); - sendToTauri('status', { message: `Updated ${selected.length} file(s).` }); - } catch (e) { - debugLog(`File update failed: ${e.message}`); - sendToTauri('status', { message: `File update failed: ${e.message}` }); - } - } + sendToTauri('status', { message: `Updating ${filesCheck.changes.length} file(s)...` }); + try { + await fileUpdater.applyFiles(filesCheck.changes, filesCheck.seq, getReplacementsConfig(), (file, done, total) => { + sendToTauri('extract-progress', { file, done, total, percent: Math.round((done / total) * 100) }); + }); + sendToTauri('status', { message: `Updated ${filesCheck.changes.length} file(s).` }); + } catch (e) { + debugLog(`File update failed: ${e.message}`); + sendToTauri('status', { message: `File update failed: ${e.message}` }); } } if (sdkCheck && sdkCheck.outdated) { - sendToTauri('status', { message: `SDK update available (${sdkCheck.latest}).` }); - const answer = await promptUser('sdk-available', { current: sdkCheck.current, latest: sdkCheck.latest }); - if (answer && answer.accept) { - try { - sendToTauri('status', { message: `Installing SDK ${sdkCheck.latest}...` }); - await fileUpdater.installSdk(sdkCheck); - sendToTauri('status', { message: `SDK updated to ${sdkCheck.latest}.` }); - } catch (e) { - debugLog(`SDK install failed: ${e.message}`); - sendToTauri('status', { message: `SDK update failed: ${e.message}` }); - } + sendToTauri('status', { message: `Updating SDK (${sdkCheck.latest})...` }); + try { + await fileUpdater.installSdk(sdkCheck); + sendToTauri('status', { message: `SDK updated to ${sdkCheck.latest}.` }); + } catch (e) { + debugLog(`SDK install failed: ${e.message}`); + sendToTauri('status', { message: `SDK update failed: ${e.message}` }); } } diff --git a/ui/index.html b/ui/index.html index 25ea7ab..23a6149 100644 --- a/ui/index.html +++ b/ui/index.html @@ -167,147 +167,7 @@ transition: width 0.35s cubic-bezier(0.2, 0.8, 0.2, 1); } - .button-group { - display: flex; - gap: 12px; - width: 100%; - justify-content: center; - margin-top: 6px; - } - .btn-apple { - position: relative; - overflow: hidden; - height: 44px; - min-width: 124px; - padding: 0 24px; - font-size: 14px; - font-weight: 600; - letter-spacing: -0.01em; - white-space: nowrap; - border-radius: 9999px; - border: none; - color: #ffffff; - background: rgba(255, 255, 255, 0.08); - text-shadow: 0 1px 8px rgba(0, 0, 0, 0.25); - cursor: pointer; - transition: translate 0.2s cubic-bezier(0.16, 1, 0.3, 1), scale 0.2s cubic-bezier(0.16, 1, 0.3, 1); - outline: none; - display: flex; - align-items: center; - justify-content: center; - } - - .btn-apple:active { - scale: 0.97; - } - - .glass-filter { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - pointer-events: none; - opacity: 0; - z-index: -1; - } - - .btn-apple.glass-svg { - background: hsl(0 0% 100% / var(--glass-frost, 0)); - backdrop-filter: var(--filter-id) saturate(var(--glass-saturation, 1)); - } - - .btn-apple.glass-frost { - background: transparent; - backdrop-filter: blur(20px) saturate(1.8); - -webkit-backdrop-filter: blur(20px) saturate(1.8); - } - - .btn-primary.glass-svg { - background: hsl(0 0% 100% / calc(var(--glass-frost, 0) + 0.14)); - } - - .btn-primary.glass-frost { - background: rgba(255, 255, 255, 0.24); - } - - .files-panel { - width: 100%; - flex-direction: column; - gap: 14px; - margin-bottom: 8px; - } - - .files-list { - max-height: 170px; - overflow-y: auto; - text-align: left; - background: rgba(255, 255, 255, 0.06); - border: 1px solid rgba(255, 255, 255, 0.1); - backdrop-filter: blur(20px) saturate(1.5); - -webkit-backdrop-filter: blur(20px) saturate(1.5); - border-radius: 18px; - padding: 8px 14px; - } - - .file-row { - display: flex; - align-items: center; - gap: 8px; - padding: 4px 0; - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - font-size: 11px; - color: rgba(255, 255, 255, 0.85); - cursor: pointer; - } - - .file-row input[type="checkbox"] { - appearance: none; - -webkit-appearance: none; - flex-shrink: 0; - width: 16px; - height: 16px; - margin: 0; - border-radius: 5px; - border: 1px solid rgba(255, 255, 255, 0.35); - background-color: rgba(255, 255, 255, 0.08); - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='%23111' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: center; - background-size: 0 0; - cursor: pointer; - outline: none; - transition: background-color 0.18s ease, border-color 0.18s ease, background-size 0.18s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.18s ease; - } - - .file-row:hover input[type="checkbox"] { - background-color: rgba(255, 255, 255, 0.16); - } - - .file-row input[type="checkbox"]:checked { - background-color: rgba(255, 255, 255, 0.95); - border-color: transparent; - background-size: 11px 11px; - } - - .file-row input[type="checkbox"]:active { - transform: scale(0.92); - } - - .file-name { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .file-tag { - font-size: 10px; - padding: 1px 8px; - border-radius: 9999px; - background: rgba(255, 255, 255, 0.25); - color: #ffffff; - } #debug-log-box { display: none; @@ -346,21 +206,6 @@
- - - @@ -1008,18 +853,7 @@ void main() { const progressTrack = document.getElementById("progress-track"); const progressFill = document.getElementById("progress-fill"); const fileLine = document.getElementById("file-line"); - const actionsEl = document.getElementById("update-actions"); - const btnYes = document.getElementById("btn-update-yes"); - const btnNo = document.getElementById("btn-update-no"); const debugBox = document.getElementById("debug-log-box"); - const filesPanel = document.getElementById("files-panel"); - const filesList = document.getElementById("files-list"); - const sdkActions = document.getElementById("sdk-actions"); - const btnFilesApply = document.getElementById("btn-files-apply"); - const btnFilesSkip = document.getElementById("btn-files-skip"); - const btnSdkYes = document.getElementById("btn-sdk-yes"); - const btnSdkNo = document.getElementById("btn-sdk-no"); - let pendingToken = null; function setStatus(text, color = "rgba(255, 255, 255, 0.95)") { if (!statusEl) return; @@ -1036,8 +870,14 @@ void main() { } function setProgress(pct) { - if (progressTrack) progressTrack.style.display = "block"; - if (progressFill) progressFill.style.width = Math.min(100, Math.max(0, pct)) + "%"; + if (!progressTrack) return; + if (pct > 0) { + progressTrack.style.display = "block"; + if (progressFill) progressFill.style.width = Math.min(100, Math.max(0, pct)) + "%"; + } else { + progressTrack.style.display = "none"; + if (progressFill) progressFill.style.width = "0%"; + } } function showDebug(msg) { @@ -1078,56 +918,10 @@ void main() { } }); - eventApi.listen("update-available", (e) => { - const data = e.payload || e; - if (data && data.latest) { - if (progressTrack) progressTrack.style.display = "none"; - setStatus(`Update Available: ${data.latest}`); - if (actionsEl) actionsEl.style.display = "flex"; - } - }); - - eventApi.listen("files-available", (e) => { - const data = e.payload || e; - if (!data || !Array.isArray(data.files)) return; - pendingToken = data.token; - if (progressTrack) progressTrack.style.display = "none"; - filesList.innerHTML = ""; - data.files.forEach((f) => { - const row = document.createElement("label"); - row.className = "file-row"; - const cb = document.createElement("input"); - cb.type = "checkbox"; - cb.checked = true; - cb.value = f.path; - const name = document.createElement("span"); - name.className = "file-name"; - name.textContent = f.path; - name.title = f.path; - const tag = document.createElement("span"); - tag.className = "file-tag"; - tag.textContent = f.status; - row.append(cb, name, tag); - filesList.appendChild(row); - }); - setStatus(`${data.files.length} file update${data.files.length === 1 ? "" : "s"} found`); - filesPanel.style.display = "flex"; - }); - - eventApi.listen("sdk-available", (e) => { - const data = e.payload || e; - if (!data || !data.latest) return; - pendingToken = data.token; - if (progressTrack) progressTrack.style.display = "none"; - setStatus(`SDK update: ${data.current} → ${data.latest}`); - sdkActions.style.display = "flex"; - }); - eventApi.listen("ready", (e) => { const data = e.payload || e; if (data && data.url) { if (progressTrack) progressTrack.style.display = "none"; - if (actionsEl) actionsEl.style.display = "none"; if (fileLine) fileLine.textContent = ""; setStatus("Launching..."); } @@ -1142,154 +936,8 @@ void main() { }); } - function answerPrompt(accept, files) { - return fetch("http://127.0.0.1:4891/prompt/answer", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ token: pendingToken, accept, files }) - }).catch((err) => { - setStatus("Failed to reach update service", "#ff6b6b"); - showDebug(err.message); - }); - } - - btnFilesApply.addEventListener("click", () => { - const picked = Array.from(filesList.querySelectorAll("input:checked")).map((i) => i.value); - filesPanel.style.display = "none"; - setStatus("Applying file updates..."); - answerPrompt(picked.length > 0, picked); - }); - - btnFilesSkip.addEventListener("click", () => { - filesPanel.style.display = "none"; - setStatus("Skipping file updates..."); - answerPrompt(false, []); - }); - - btnSdkYes.addEventListener("click", () => { - sdkActions.style.display = "none"; - setStatus("Updating SDK..."); - answerPrompt(true, []); - }); - - btnSdkNo.addEventListener("click", () => { - sdkActions.style.display = "none"; - setStatus("Skipping SDK update..."); - answerPrompt(false, []); - }); - - btnYes.addEventListener("click", () => { - if (actionsEl) actionsEl.style.display = "none"; - setStatus("Starting update installation..."); - fetch("http://127.0.0.1:4891/update/yes", { method: "POST" }).catch((err) => { - setStatus("Failed to reach update service", "#ff6b6b"); - showDebug(err.message); - }); - }); - - btnNo.addEventListener("click", () => { - if (actionsEl) actionsEl.style.display = "none"; - setStatus("Skipping update. Starting services..."); - fetch("http://127.0.0.1:4891/update/no", { method: "POST" }).catch(() => { }); - }); - - function applyGlassSurface(el, cfg) { - const c = Object.assign({ - borderRadius: 38, - borderWidth: 0.07, - brightness: 50, - opacity: 0.93, - blur: 11, - displace: 0, - backgroundOpacity: 0, - saturation: 1, - distortionScale: -180, - redOffset: 0, - greenOffset: 10, - blueOffset: 20, - mixBlendMode: 'difference' - }, cfg); - const id = 'glass-' + Math.random().toString(36).slice(2, 9); - const ua = navigator.userAgent; - const blocked = (/Safari/.test(ua) && !/Chrome/.test(ua)) || /Firefox/.test(ua); - const probe = document.createElement('div'); - probe.style.backdropFilter = 'url(#' + id + ')'; - const supported = !blocked && probe.style.backdropFilter !== ''; - - el.style.setProperty('--glass-frost', c.backgroundOpacity); - el.style.setProperty('--glass-saturation', c.saturation); - - if (!supported) { - el.classList.add('glass-frost'); - return; - } - - const channel = (name, offset, matrix) => - '