From 10362e204a56fc9f7b012ac8d26c2b3587ee454e Mon Sep 17 00:00:00 2001 From: claude-bot Date: Thu, 16 Jul 2026 00:09:51 +0900 Subject: [PATCH] =?UTF-8?q?fix(installer):=20=EC=95=BD=EA=B4=80=20?= =?UTF-8?q?=EB=B3=B8=EB=AC=B8=20=EB=A1=9C=EB=93=9C=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EC=8B=9C=20=EB=8F=99=EC=9D=98=20=EC=B0=A8=EB=8B=A8=20+=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EC=8B=9C=EB=8F=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로드 실패 경로에서 markReadable()로 체크박스가 활성화돼, 약관을 못 본 채로 동의할 수 있던 문제 수정. termLoaded 플래그를 두어 본문이 정상 로드된 뒤에만 동의 체크를 허용하고, 실패 시에는 체크/다음을 막고 "다시 시도" 버튼만 노출. installer / installer-rp 동일 적용. 버전 0.4.1 → 0.4.2. Co-Authored-By: Claude Opus 4.7 --- installer-rp/renderer.js | 40 +++++++++++++++++++++++++++++++--------- installer/renderer.js | 40 +++++++++++++++++++++++++++++++--------- package.json | 2 +- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/installer-rp/renderer.js b/installer-rp/renderer.js index 5e789cb..4cb0bdc 100644 --- a/installer-rp/renderer.js +++ b/installer-rp/renderer.js @@ -226,8 +226,12 @@ function renderAgreementWithKinds(KINDS) { // 새 약관 페이지는 화면 맨 위에서부터 보이도록 스크롤을 올린다. if (pageHost) pageHost.scrollTop = 0 + // 약관 본문이 정상 로드된 뒤에만 동의를 허용한다. 로드 실패 상태에서는 + // 스크롤을 해도 동의 체크가 켜지지 않는다("무조건 약관을 읽도록"). + var termLoaded = false + function markReadable() { - if (!accept.disabled) return + if (!termLoaded || !accept.disabled) return accept.disabled = false hint.textContent = '' } @@ -257,6 +261,7 @@ function renderAgreementWithKinds(KINDS) { }) function afterLoad() { + termLoaded = true body.scrollTop = 0 if (accepted[k.id]) { accept.disabled = false @@ -270,15 +275,31 @@ function renderAgreementWithKinds(KINDS) { }, 0) } - if (cache[k.id]) { - body.innerHTML = cache[k.id] - afterLoad() - } else { + // 약관 본문 로드 실패: 동의 불가 상태를 유지하고 다시 시도만 허용한다. + function showLoadError(message) { + termLoaded = false + accept.disabled = true + accept.checked = false + accepted[k.id] = false + nextBtn.disabled = true + hint.textContent = tt('agreement.readToBottom') + body.innerHTML = + '

' + escapeHtml(tt('agreement.loadFailed', { message: message || '' })) + '

' + + '
' + var retry = section.querySelector('#agRetry') + if (retry) retry.addEventListener('click', loadTerm) + } + + function loadTerm() { + if (cache[k.id]) { + body.innerHTML = cache[k.id] + afterLoad() + return + } body.textContent = tt('agreement.loading') api.getTerm(k.id).then(function (res) { if (!res.ok) { - body.innerHTML = '

' + escapeHtml(tt('agreement.loadFailed', { message: res.message || '' })) + '

' - markReadable() + showLoadError(res.message || '') return } var html = renderTermsMarkdown(res.content || '') @@ -286,10 +307,11 @@ function renderAgreementWithKinds(KINDS) { body.innerHTML = html afterLoad() }).catch(function (err) { - body.innerHTML = '

' + escapeHtml(tt('agreement.loadFailed', { message: err.message })) + '

' - markReadable() + showLoadError(err && err.message ? err.message : '') }) } + + loadTerm() } renderCurrent() diff --git a/installer/renderer.js b/installer/renderer.js index 6da0a9a..eb56145 100644 --- a/installer/renderer.js +++ b/installer/renderer.js @@ -266,8 +266,12 @@ function renderAgreementWithKinds(KINDS) { // 새 약관 페이지는 화면 맨 위에서부터 보이도록 스크롤을 올린다. if (pageHost) pageHost.scrollTop = 0 + // 약관 본문이 정상 로드된 뒤에만 동의를 허용한다. 로드 실패 상태에서는 + // 스크롤을 해도 동의 체크가 켜지지 않는다("무조건 약관을 읽도록"). + var termLoaded = false + function markReadable() { - if (!accept.disabled) return + if (!termLoaded || !accept.disabled) return accept.disabled = false hint.textContent = '' } @@ -298,6 +302,7 @@ function renderAgreementWithKinds(KINDS) { }) function afterLoad() { + termLoaded = true body.scrollTop = 0 // 이미 동의했던 약관으로 되돌아온 경우: 체크/다음 활성화 유지. if (accepted[k.id]) { @@ -313,15 +318,31 @@ function renderAgreementWithKinds(KINDS) { }, 0) } - if (cache[k.id]) { - body.innerHTML = cache[k.id] - afterLoad() - } else { + // 약관 본문 로드 실패: 동의 불가 상태를 유지하고 다시 시도만 허용한다. + function showLoadError(message) { + termLoaded = false + accept.disabled = true + accept.checked = false + accepted[k.id] = false + nextBtn.disabled = true + hint.textContent = tt('agreement.readToBottom') + body.innerHTML = + '

' + tt('agreement.loadFailed', { message: message || '' }) + '

' + + '
' + var retry = section.querySelector('#agRetry') + if (retry) retry.addEventListener('click', loadTerm) + } + + function loadTerm() { + if (cache[k.id]) { + body.innerHTML = cache[k.id] + afterLoad() + return + } body.textContent = tt('agreement.loading') installerApi.getTerm(k.id).then(function (res) { if (!res.ok) { - body.innerHTML = '

' + tt('agreement.loadFailed', { message: res.message || '' }) + '

' - markReadable() + showLoadError(res.message || '') return } var html = renderTermsMarkdown(res.content || '') @@ -329,10 +350,11 @@ function renderAgreementWithKinds(KINDS) { body.innerHTML = html afterLoad() }).catch(function (err) { - body.innerHTML = '

' + tt('agreement.loadFailed', { message: err.message }) + '

' - markReadable() + showLoadError(err && err.message ? err.message : '') }) } + + loadTerm() } renderCurrent() diff --git a/package.json b/package.json index cc30d96..a70ce44 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minecraft-music-quiz-installer", - "version": "0.4.1", + "version": "0.4.2", "description": "마인크래프트 음악퀴즈 간편설치기 + 관리 사이트", "main": "dist/installer/main.js", "scripts": {