'use strict' const api = window.uninstaller let I18N = {} function tt(key, params) { var parts = String(key).split('.') var cur = I18N for (var i = 0; i < parts.length; i++) { if (cur && typeof cur === 'object' && parts[i] in cur) { cur = cur[parts[i]] } else { return key } } if (typeof cur !== 'string') return key if (!params) return cur return cur.replace(/\{\{\s*(\w+)\s*\}\}/g, function (_m, name) { return name in params ? String(params[name]) : '{{' + name + '}}' }) } function escapeHtml(s) { return String(s).replace(/[&<>"']/g, function (c) { return c === '&' ? '&' : c === '<' ? '<' : c === '>' ? '>' : c === '"' ? '"' : ''' }) } const pageHost = document.getElementById('pageHost') const logViewer = document.getElementById('logViewer') const logBody = document.getElementById('logBody') const logToggle = document.getElementById('logToggle') logToggle.addEventListener('click', function () { logViewer.classList.toggle('collapsed') if (logViewer.classList.contains('collapsed')) { logViewer.style.height = '36px' logToggle.textContent = tt('logViewer.expand') } else { logViewer.style.height = '' logToggle.textContent = tt('logViewer.collapse') } }) api.onLog(function (line) { logViewer.hidden = false logBody.textContent += line + '\n' logBody.scrollTop = logBody.scrollHeight }) function applyStaticI18n() { document.title = tt('app.title') var h1 = document.querySelector('.appHeader h1') if (h1) h1.textContent = tt('app.title') var logH2 = logViewer.querySelector('header h2') if (logH2) logH2.textContent = tt('logViewer.heading') logToggle.textContent = tt('logViewer.collapse') } // ── 1단계: 삭제 동의 ────────────────────────────── function renderConfirm() { pageHost.innerHTML = '
' + '

' + escapeHtml(tt('confirm.heading')) + '

' + '

' + escapeHtml(tt('confirm.question')) + '

' + '

' + escapeHtml(tt('confirm.detail')) + '

' + '

' + escapeHtml(tt('confirm.loadingPreview')) + '

' + '
' + ' ' + ' ' + '
' + '
' var agreeBtn = document.getElementById('agreeBtn') var cancelBtn = document.getElementById('cancelBtn') var previewBox = document.getElementById('previewBox') cancelBtn.addEventListener('click', function () { api.quit() }) api.preview().then(function (p) { var existingDirs = p.existingDirs || [] var items = [] if (existingDirs.length) { for (var d = 0; d < existingDirs.length; d++) { items.push(tt('confirm.itemCustomDir', { path: existingDirs[d] })) } } else { var first = (p.allTargetDirs && p.allTargetDirs[0]) || '' items.push(tt('confirm.itemCustomDirMissing', { path: first })) } if (p.shortcutExists) items.push(tt('confirm.itemShortcut')) if (p.launcherProfiles && p.launcherProfiles.length) { items.push(tt('confirm.itemProfiles', { names: p.launcherProfiles.join(', ') })) } var nothing = !existingDirs.length && !p.shortcutExists && (!p.launcherProfiles || !p.launcherProfiles.length) var html = '

' + escapeHtml(tt('confirm.previewTitle')) + '

' if (nothing) html += '

' + escapeHtml(tt('confirm.nothingFound')) + '

' previewBox.innerHTML = html agreeBtn.disabled = false agreeBtn.addEventListener('click', function () { renderChoose(p) }) }).catch(function (err) { previewBox.innerHTML = '

' + escapeHtml(tt('confirm.previewFail', { message: (err && err.message) || String(err) })) + '

' agreeBtn.disabled = false agreeBtn.addEventListener('click', function () { renderChoose({ existingDirs: [], allTargetDirs: [], shortcutExists: false, launcherProfiles: [] }) }) }) } // ── 2단계: 삭제 방식 선택 ───────────────────────── function renderChoose(preview) { pageHost.innerHTML = '
' + '

' + escapeHtml(tt('choose.heading')) + '

' + '

' + escapeHtml(tt('choose.intro')) + '

' + '
' + ' ' + ' ' + '
' + '
' + ' ' + '
' + '
' + '
' var trashBtn = document.getElementById('trashBtn') var permBtn = document.getElementById('permBtn') var backBtn = document.getElementById('backBtn') var runState = document.getElementById('runState') backBtn.addEventListener('click', function () { renderConfirm() }) function runMode(mode) { var warn = mode === 'permanent' ? tt('choose.confirmPermanent') : tt('choose.confirmTrash') if (!window.confirm(warn)) return trashBtn.disabled = true permBtn.disabled = true backBtn.disabled = true runState.innerHTML = '

' + escapeHtml(tt('choose.running')) + '

' api.run(mode).then(function (result) { renderResult(result, mode) }).catch(function (err) { runState.innerHTML = '

' + escapeHtml(tt('choose.error', { message: (err && err.message) || String(err) })) + '

' trashBtn.disabled = false permBtn.disabled = false backBtn.disabled = false }) } trashBtn.addEventListener('click', function () { runMode('trash') }) permBtn.addEventListener('click', function () { runMode('permanent') }) } // ── 3단계: 결과 ────────────────────────────────── function renderResult(result, mode) { var total = (result.removed ? result.removed.length : 0) + (result.profilesRemoved ? result.profilesRemoved.length : 0) var hasErr = result.errors && result.errors.length var cls = hasErr ? 'error' : 'done' var badge = hasErr ? tt('result.badgePartial') : tt('result.badgeOk') var lines = '' if (result.removed && result.removed.length) { lines += '

' + escapeHtml(tt('result.removedTitle')) + '

' } if (result.profilesRemoved && result.profilesRemoved.length) { lines += '

' + escapeHtml(tt('result.profilesTitle')) + '

' } if (hasErr) { lines += '

' + escapeHtml(tt('result.errorsTitle')) + '

' } if (total === 0 && !hasErr) { lines += '

' + escapeHtml(tt('result.nothing')) + '

' } pageHost.innerHTML = '
' + '
' + '
' + escapeHtml(badge) + ' ' + ' ' + escapeHtml(tt(mode === 'permanent' ? 'mode.permanent' : 'mode.trash')) + '
' + lines + '

' + escapeHtml(tt('result.note')) + '

' + '
' + '
' + ' ' + '
' + '
' document.getElementById('quitBtn').addEventListener('click', function () { api.quit() }) } ;(async function () { try { I18N = (await api.loadLocale()) || {} } catch (_) { I18N = {} } applyStaticI18n() renderConfirm() })()