const state = { manifestUrl: '', selectedPack: null, selectedPackMeta: null, installPath: '', jdkPath: '' } const panelMap = new Map([...document.querySelectorAll('.panel')].map((panel) => [panel.dataset.panel, panel])) const stepMap = new Map([...document.querySelectorAll('.steps li')].map((step) => [step.dataset.step, step])) const logView = document.getElementById('logView') const packList = document.getElementById('packList') const manifestUrlInput = document.getElementById('manifestUrl') const installPathInput = document.getElementById('installPath') const jdkPathInput = document.getElementById('jdkPath') const jdkStatus = document.getElementById('jdkStatus') const jdkRecommended = document.getElementById('jdkRecommended') const eulaBlock = document.getElementById('eulaBlock') const eulaText = document.getElementById('eulaText') const eulaLink = document.getElementById('eulaLink') const startInstallButton = document.getElementById('startInstallButton') const applyClientButton = document.getElementById('applyClientButton') const clientApplyStatus = document.getElementById('clientApplyStatus') function setActiveStep(step) { for (const [key, panel] of panelMap.entries()) { panel.classList.toggle('active', key === String(step)) } for (const [key, item] of stepMap.entries()) { item.classList.toggle('active', key === String(step)) } } function appendLog(entry) { if (entry?.action === 'eula-required') { eulaText.textContent = entry.eulaText ?? '' eulaLink.href = entry.eulaUrl ?? '#' eulaBlock.classList.remove('hidden') return } const line = `[${entry?.tone ?? 'info'}] ${entry?.message ?? ''}` logView.textContent += `${line}\n` logView.scrollTop = logView.scrollHeight } function validateInstallPath(pathValue) { const warning = document.getElementById('installPathWarning') const hasHangul = /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/.test(pathValue) warning.textContent = hasHangul ? '경로에 한글이 포함되면 안 됩니다.' : '' return !hasHangul && pathValue.trim().length > 0 } function renderPackList(packs) { packList.innerHTML = '' packs.forEach((pack) => { const label = document.createElement('label') label.className = 'packOption' label.innerHTML = `
${pack.name} ${pack.file}
` packList.appendChild(label) }) } async function loadPackManifest() { state.manifestUrl = manifestUrlInput.value.trim() state.selectedPack = null state.selectedPackMeta = null packList.innerHTML = '
서버팩 목록을 불러오는 중입니다.
' try { const manifest = await window.installerApi.loadPacks(state.manifestUrl) renderPackList(manifest.packs) } catch (error) { packList.innerHTML = `
${error.message}
` } } async function loadSelectedPackMeta() { if (state.selectedPack == null) { state.selectedPackMeta = null return null } const packMeta = await window.installerApi.inspectPack(state.manifestUrl, state.selectedPack.file) state.selectedPackMeta = packMeta return packMeta } async function autoDetectJdkForSelectedPack() { const packMeta = await loadSelectedPackMeta() if (packMeta == null) { jdkRecommended.textContent = '먼저 서버팩을 선택하세요.' jdkStatus.textContent = '' return } const recommendedVersion = packMeta.packDefinition.recommendedJdkVersion ?? null jdkRecommended.textContent = recommendedVersion != null ? `선택한 서버팩의 권장 JDK 버전: ${recommendedVersion}` : '선택한 서버팩에 권장 JDK 버전 정보가 없습니다.' jdkStatus.textContent = 'JDK 자동 탐색 중입니다.' const result = await window.installerApi.detectJdk(recommendedVersion) if (result.detected != null) { state.jdkPath = result.detected jdkPathInput.value = result.detected } if (result.detected == null) { jdkStatus.textContent = '설치 가능한 JDK를 찾지 못했습니다.' return } const pickedCandidate = result.candidates.find((candidate) => candidate.path === result.detected) const versionLabel = pickedCandidate?.majorVersion != null ? `JDK ${pickedCandidate.majorVersion}` : '버전 미확인 JDK' if (result.recommendedVersion != null && result.exactMatch) { jdkStatus.textContent = `권장 버전과 일치하는 ${versionLabel}를 자동 선택했습니다: ${result.detected}` return } if (result.recommendedVersion != null) { jdkStatus.textContent = `권장 JDK ${result.recommendedVersion}은 찾지 못해 ${versionLabel}를 대신 선택했습니다: ${result.detected}` return } jdkStatus.textContent = `자동 탐색 성공: ${versionLabel} / ${result.detected}` } async function goToStep(step) { setActiveStep(step) if (step === 3) { try { await autoDetectJdkForSelectedPack() } catch (error) { jdkStatus.textContent = error.message } } } async function bootstrap() { const defaults = await window.installerApi.getDefaults() state.manifestUrl = defaults.manifestUrl manifestUrlInput.value = defaults.manifestUrl await loadPackManifest() } window.installerApi.onLog(appendLog) packList.addEventListener('change', () => { const checked = packList.querySelector('input[name="packChoice"]:checked') if (checked == null) { state.selectedPack = null state.selectedPackMeta = null return } state.selectedPack = { file: checked.value, name: checked.dataset.packName ?? checked.value } state.selectedPackMeta = null }) document.querySelectorAll('[data-back]').forEach((button) => { button.addEventListener('click', async () => { await goToStep(button.dataset.back) }) }) manifestUrlInput.addEventListener('change', async () => { await loadPackManifest() }) document.getElementById('toStep2').addEventListener('click', async () => { if (state.selectedPack == null) { alert('서버팩을 먼저 선택하세요.') return } await goToStep(2) }) document.getElementById('browseInstallPath').addEventListener('click', async () => { const selected = await window.installerApi.chooseDirectory() if (selected != null) { state.installPath = selected installPathInput.value = selected validateInstallPath(selected) } }) installPathInput.addEventListener('input', (event) => { state.installPath = event.target.value validateInstallPath(state.installPath) }) document.getElementById('toStep3').addEventListener('click', async () => { if (!validateInstallPath(state.installPath)) { alert('올바른 설치 경로를 입력하세요.') return } await goToStep(3) }) document.getElementById('browseJdkPath').addEventListener('click', async () => { const selected = await window.installerApi.chooseJdk() if (selected != null) { state.jdkPath = selected jdkPathInput.value = selected } }) jdkPathInput.addEventListener('input', (event) => { state.jdkPath = event.target.value }) document.getElementById('toStep4').addEventListener('click', async () => { if (state.jdkPath.trim().length === 0) { alert('JDK 경로를 지정하세요.') return } await goToStep(4) }) startInstallButton.addEventListener('click', async () => { if (state.selectedPack == null) { alert('서버팩을 먼저 선택하세요.') return } logView.textContent = '' eulaBlock.classList.add('hidden') eulaText.textContent = '' startInstallButton.disabled = true try { const result = await window.installerApi.startInstall({ manifestUrl: state.manifestUrl, packFile: state.selectedPack.file, installPath: state.installPath, jdkPath: state.jdkPath }) if (result.warning != null) { appendLog({ message: result.warning, tone: 'warn' }) } await goToStep(result.nextStep) } finally { startInstallButton.disabled = false } }) document.getElementById('acceptEulaButton').addEventListener('click', async () => { await window.installerApi.acceptEula() eulaBlock.classList.add('hidden') }) document.getElementById('openConfigEditorButton').addEventListener('click', async () => { const url = await window.installerApi.openConfigEditor() document.getElementById('configEditorStatus').textContent = `브라우저에서 열림: ${url}` }) document.getElementById('toStep6').addEventListener('click', async () => { await goToStep(6) }) document.getElementById('configurePortButton').addEventListener('click', async () => { const result = await window.installerApi.configurePort() document.getElementById('portStatusBox').textContent = result.message }) document.getElementById('toStep7').addEventListener('click', async () => { await goToStep(7) }) applyClientButton.addEventListener('click', async () => { applyClientButton.disabled = true clientApplyStatus.textContent = '클라이언트 적용 중입니다.' try { const result = await window.installerApi.applyClient() clientApplyStatus.textContent = result.message await goToStep(result.nextStep) } finally { applyClientButton.disabled = false } }) document.getElementById('openFolderButton').addEventListener('click', async () => { await window.installerApi.openFolder() }) document.getElementById('finishButton').addEventListener('click', async () => { const createShortcut = document.getElementById('createShortcutToggle').checked const runServer = document.getElementById('runServerToggle').checked await window.installerApi.createShortcut(createShortcut) await window.installerApi.runServer(runServer) alert('설치가 완료되었습니다.') }) bootstrap()