181 Commits

Author SHA1 Message Date
05a5dcedc0 installer-pf: guide manual forwarding + detect CGNAT on failure
포트포워딩 도구가 실패/확인불가일 때 다음 조치를 바로 안내하도록 보강.
- 이 PC LAN IPv4 자동 감지 → "외부 TCP {port} → {localIp}:{port} 수동 포워딩 +
  Windows 방화벽 인바운드 허용" 안내.
- 외부 IP 가 CGNAT(100.64/10) 대역이면 경고(공유기 포워딩만으론 불가).
- UPnP 가 ECONNREFUSED/타임아웃이면 "라우터 UPnP 거부/미지원" 로그 힌트.
실제 사용자 로그(라우터 UPnP ECONNREFUSED, 공인 IP, ifconfig false)에 기반한
개선. v0.3.16.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.16
2026-07-05 21:13:19 +09:00
86883f56e4 add standalone port-forwarding tool (마인크래프트 간편 포트포워딩)
포트포워딩만 단독으로 수행하는 세 번째 Electron 앱 추가.
- 포트 입력(기본 25565) → UPnP 개방 시도 → 외부에서 닿는지 점검(ifconfig.co
  + 임시 리스너) → 외부 접속 주소/성공·실패·확인불가 안내. '포트 닫기'로 매핑 제거.
- 점검 로직은 메인 설치기의 v0.3.14 오탐 수정(ifconfig 실패→확인불가)을 반영해
  자체 포함. sharp 미사용이라 빌드에서 제외(exe 약 73MB).
- 신규: src/installer-pf/{main,preload}.ts, installer-pf/{index.html,renderer.js},
  locales/installer-pf/ko-kr.json, tsconfig.installer-pf.json, electron-builder-pf.yml.
- package.json 에 installer:pf / dist:win:pf 스크립트, i18n 컴포넌트 유니온에
  'installer-pf' 추가. v0.3.15.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.15
2026-06-24 21:43:06 +09:00
f554559be9 installer: stop treating ifconfig.co failure as 'port closed'
포트 1차 점검(probePortFromOutside) 오탐 수정.
- 임시 리스너에 인바운드가 안 왔다는 이유만으로 false(닫힘) 단정하던 로직 제거.
  ifconfig.co 가 타임아웃/레이트리밋으로 실패하면 외부에서 연결 시도 자체가
  없었던 것이라 '리스너 미도달'은 무의미하고, 인바운드 수신 주체가 마크 서버가
  아니라 설치기 프로세스라 Windows 방화벽이 설치기만 막아도 미도달이 된다.
  외부 판정이 없으면 reachable=null(확인 불가)로 남겨 UPnP 시도/안내로 넘긴다.
- ifconfig.co 일시 실패 대비 1회 재시도 추가(fetchIfconfigCoPortOnce + 래퍼).
v0.3.14.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.14
2026-06-24 21:24:12 +09:00
8c0226d3f9 installer: fix UPnP PowerShell parse error (^| -> | inside quotes)
run.bat 의 UPnP 등록/해제 PowerShell 명령에서 파이프를 cmd식 ^| 로
이스케이프했는데, 이 파이프는 powershell -Command "..." 의 큰따옴표 안이라
cmd 가 이미 리터럴로 넘긴다. 그래서 PowerShell 이 ^ 를 "예기치 않은 토큰"
으로 파싱 실패(서버는 정상 기동하나 콘솔에 에러 출력, UPnP 매핑도 실패).
^| -> | 로 교정. for/f 의 2^>nul(=cmd 레벨 리다이렉트)은 그대로 유지. v0.3.13.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.13
2026-06-24 17:09:39 +09:00
674b9e7c87 installers: add intro notice (main) and finish notice (rp)
- 메인 설치기: 첫 페이지로 "마인크래프트 런처를 끄고 시작해주세요" 안내(renderIntro)
  추가 후 다음 버튼으로 step1 진입.
- 리소스팩 설치기: 완료(step3) 페이지 문구를 "사용자 동의하에 리소스팩이
  설치되었습니다." + "리소스팩은 직접 적용해주세요." 로 변경.
v0.3.12.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.12
2026-06-23 23:02:38 +09:00
4a76c09f3a installer: brighten agreement text (white→pure white, gray→white)
약관 본문(.agreementBody) 글자색을 더 진하게: 제목/굵은글씨/목록은 순백(#fff),
회색이던 문단(.page p 상속)은 이전 흰색 톤(#e6edf3)으로 올림. 두 설치기가
공유하는 installer/styles.css 변경. v0.3.11.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.11
2026-06-23 22:54:51 +09:00
48aec4e144 site: add video field and set volume default 0.5 in datapack SNBT
곡 SNBT 출력을 {volume:0.5, title, author, alias, description, video} 형식으로
변경(요청). video = MusicListEntry.url(유튜브 영상 주소). volume 기본값을
1.0 → 0.5 로 변경. 헤더 안내 주석도 새 형식에 맞춤.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-14 01:39:55 +09:00
21eadb3b20 site: reorder datapack SNBT fields to volume-first
데이터팩 songs.mcfunction 의 곡 SNBT 출력 순서를
{volume, title, author, alias, description} 로 변경(요청). volume 기본값은
1.0 유지. SNBT 는 키 순서 무관이라 데이터팩 파싱에는 영향 없음.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-14 01:35:59 +09:00
fa5da6d052 installer-rp: fix ffmpeg 404 by using rolling 'latest' tag URL
BtbN/FFmpeg-Builds 다운로드를 releases/latest/download/ (GitHub 최신 릴리스
자동 포인터)에서 releases/download/latest/ (항상 최신 자산이 붙은 롤링 latest
태그)로 변경. 전자는 갓 생성된 autobuild-<날짜> 릴리스로 리다이렉트되는데
자산이 아직/없으면 HTTP 404 로 ffmpeg 설치가 실패한다. v0.3.10.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.10
2026-06-07 23:54:18 +09:00
f6df5f936c installer-rp: retry image download on HTTP 429/5xx with backoff
i.ytimg.com 썸네일 서버가 연속 요청을 속도제한(HTTP 429)하면 사진
다운로드가 즉시 실패해 전체 설치가 중단됐다. 일시적 상태코드
(408/425/429/5xx)와 네트워크 오류를 Retry-After 우선 + 지수 백오프(jitter)로
최대 5회 재시도하도록 fetchBuffer 를 보강. v0.3.9.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.9
2026-06-07 23:36:30 +09:00
dfb7acba2f installer: use 128x128 launcher profile icon (correct spec)
마인크래프트 런처 사용자 지정 설치 아이콘 규격은 128x128 PNG 고정이다
(minecraft.wiki/w/Launcher). 64x64/256x256 등 규격과 다른 크기는 런처가
무시하고 기본 아이콘(화로)으로 폴백한다. ICON_SIZE 를 128 로 맞춰 음악
아이콘이 실제로 표시되게 한다. v0.3.8.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.8
2026-06-07 23:04:34 +09:00
f4c9504c1a installer: shrink launcher profile icon to 64x64 data URL
256x256(~44KB base64) 아이콘은 일부 마인크래프트 런처에서 렌더링되지 않고
기본 아이콘(화로)으로 폴백한다. 프로필 아이콘은 작은 타일로 표시되므로
sharp 로 64x64(~8KB base64) 로 다운스케일해 안정적으로 표시되게 한다.
exe 아이콘(build/icon.*)은 256x256 그대로 유지. v0.3.7.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.7
2026-06-07 22:54:48 +09:00
60a52a9bec installer-rp: delete partial artifacts on failure; bump to 0.3.6
Resume previously skipped any track/cover whose file merely existed, so a
partially written NN.ogg or cover_NN.png from a failed download/convert
could be mistaken for a finished file on the next attempt. Now the
failure path removes the expected output before bailing, so only fully
completed artifacts are skipped on resume.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
v0.3.6
2026-06-05 16:30:45 +09:00
fe0d2f75e3 installer-rp: add resume-on-retry and discard-on-quit for failed installs
On install failure the temp folder is now preserved instead of wiped, so
already-downloaded songs/images are skipped on the next attempt. The
error screen offers 재시도 (resume from the failed item) and 처음으로
(discard the partial download and restart). Closing the program without
retrying still wipes the partial download via window-all-closed, and an
explicit cancel also clears it.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-05 16:23:34 +09:00
399f4af808 installer-rp: defer yt-dlp/ffmpeg reinstall until all music workers finish
Avoid the Windows file-lock race where one worker deletes/overwrites
yt-dlp.exe/ffmpeg.exe while sibling workers still run those processes.
Now pass 1 downloads all tracks and collects failures without any
mid-flight refresh; after Promise.all (no live child processes), the
binaries are force-reinstalled once and only the failed tracks retry.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-05 16:18:12 +09:00
d5f88e0e76 yt-dlp/ffmpeg: reinstall latest on failure, retry once
오래된 yt-dlp/ffmpeg 가 유튜브 변경을 못 따라가 다운로드가 실패할 때
최신 버전으로 강제 재설치 후 한 번 더 시도한다.

- server youtube.ts: ensureYtDlp(force) 추가(캐시·zipapp 삭제 후 최신 재다운로드).
  fetchVideoMeta/fetchPlaylistEntries 를 runYtDlp 로 묶어 1차 실패 시
  강제 재설치 후 재시도.
- installer ytdlp.ts/ffmpeg.ts: ensure*Exe(log, force) 추가.
- installer main.ts: 음악 워커가 곡 다운로드 실패 시 전역 1회 강제 재설치
  (refreshBinariesOnce) 후 해당 곡을 1회 재시도.
2026-06-05 16:08:26 +09:00
d9ba2b0f35 installer-rp: decode data: URL images instead of crashing
사진 URL 에 data: URI 가 들어오면 http/https 만 처리하는 다운로더가
'Protocol "data:" not supported' 로 설치 전체를 중단시키던 문제 수정.
data: URL 은 이미지 바이트를 직접 품고 있으므로 base64/percent-encoding
을 디코드해 Buffer 로 바로 반환한다. 잘못된 형식은 명확한 메시지로 거절.
2026-06-05 15:58:22 +09:00
3baf84cfd1 op: emit painting_variant author/title as plain strings
이미지 zip 의 cover_NN.json 이 title/author 를 {text:...} 객체로 내보내
일부 환경에서 인식되지 않던 문제. 요청 형식대로 author:"musicquiz",
title:"cover_NN" 평문 문자열로 바꿔 asset_id/width/height 뒤에 배치한다.
2026-06-05 15:51:59 +09:00
d22c6f17a3 store: rename file/list JSON when pack key changes
renamePack 가 manifest 정의와 약관 폴더는 새 키로 옮기면서 정작 음악·사진
목록(file/list/<key>.json)은 옛 키 파일에 남겨, 이름 변경 후 목록이 비어
보이던 버그 수정. 약관 폴더와 동일하게 fsp.rename 으로 옮기고 옛 파일이
없으면(ENOENT) 무시한다.
2026-06-05 01:57:30 +09:00
0629aa54aa datapack: emit volume:1.0 default on every SNBT entry
운영자가 곡별로 /playsound 음량을 빠르게 조정할 수 있도록
launcher 가 생성하는 모든 SNBT 항목에 volume:1.0 기본값을 항상 넣는다.
주석의 예시도 volume:1.0 으로 통일.
2026-05-28 00:37:37 +09:00
201043e289 datapack: include description in SNBT entry output
entrySnbt() now emits {title, author, alias, description} so the
mcfunction export carries the operator-entered song description
into the data modify storage command. Multiline descriptions are
flattened via newline/tab escapes inside the SNBT string literal
(escapeSnbtString extended to handle \r, \n, \t alongside the
existing backslash + quote escapes) so each `data modify` stays a
single line.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 00:28:37 +09:00
acd3dd995d list-editor: preserve aliases + description across URL edit
The url-edit modal's save handler was rebuilding state.music[idx]
from scratch using only meta-lookup fields, silently dropping aliases
and (newly added) description. Carry them over from prev so editing
a track's URL no longer wipes operator-entered metadata.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:48:55 +09:00
b4160aefc1 list-editor: add per-track description button + modal
Each music list row now shows a 설명 button immediately to the left of
the 별칭 button. Click opens a modal with a multi-line textarea; on
close the value is persisted into MusicListEntry.description and saved
to the same pack list JSON. The button gets a hasDesc visual indicator
when filled. Description is stored but intentionally not consumed by
datapack export or alias matching — purely informational metadata.

- types.ts: add description: string to MusicListEntry
- store.ts: normalize entry.description via sanitizeStr (defaults to '')
- listEditor.ejs: new #descModal alongside aliasModal
- listEditor.js: render descBtn left of aliasBtn, attach handlers,
  also set description: '' on playlist-fetched entries
- styles.css: extend trackRow grid to 6 cols, reuse aliasBtn styling
  for descBtn, add descTextarea sizing
- locale (ko-kr): descBtn / descModalTitle / descBack / descPlaceholder
  / descHint

Backwards-compatible: existing list JSON files without description
field normalize to ''.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 20:44:24 +09:00
1ac13a03ff server-youtube: fast-path reuse for cached zipapp
ensureYtDlp() and prepareYtDlp() now check the on-disk yt-dlp_zipapp
before re-running the native-fail -> network-download path. On Linux
servers where the native binary always fails verification (glibc/musl/
arch mismatch), every previous request was re-downloading both the
native (~33MB) and the zipapp (~3MB). With the fast path, after the
first successful zipapp install all subsequent requests short-circuit
to the cached zipapp.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:39:10 +09:00
542f759585 chore: remove stray 0-byte garbage file from repo root
Accidentally tracked by the previous commit's git add -A. Untracked
artifact from shell-command mangling in an earlier smoke test, not
part of any feature.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:35:30 +09:00
3248d096e4 server-youtube: add POSIX zipapp fallback when native bundled binary won't run
If the native yt-dlp_linux/yt-dlp_macos binary fails to execute (glibc
mismatch, musl libc, wrong arch) AND no system yt-dlp is on PATH, fall
back to downloading the universal Python zipapp ('yt-dlp', ~3MB) and
running it via shebang. Requires python3 on PATH, which is standard on
modern Linux servers. Also: rewrite stale Windows-flavored install-path
comments to reflect actual cross-platform behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:35:11 +09:00
8c9dc88e8b server-youtube: strip Zone.Identifier ADS on Windows after download
NTFS marks files downloaded over HTTP with a Zone.Identifier alternate
data stream, which SmartScreen/Attachment Manager can use to block
execution of yt-dlp.exe. Remove the ADS best-effort after each
download to reduce one likely cause of "execution verification failed"
in the user-reported failure.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:29:27 +09:00
b769f453a3 server-youtube: diagnostic detail + PATH fallback when bundled yt-dlp won't run
probeVersion() now captures stderr/exit-code/signal/spawn-error instead of
returning a bare boolean, and ensureYtDlp() tries the bundled binary first,
falls back to `yt-dlp(.exe)` on PATH if the bundled one won't execute (AV
block, missing libc symbol, broken download), and only then re-downloads.
The final user-facing error includes the per-attempt diagnostics so we can
actually see WHY verification failed instead of the opaque
"yt-dlp 다운로드는 됐지만 실행 검증에 실패했습니다." message.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 16:24:54 +09:00
5c13648f63 rp-pack: fail-fast on base track/painting collision (was: silent skip)
Reviewer correctly flagged that the previous skip-on-collision
behavior silently drops new quiz tracks when the base resourcepack
already has the same track_NN key. That makes the install LOOK
successful but breaks the quiz at runtime (datapack references the
missing track).

The new behavior throws a clear error explaining which key collided
and what the user must do (remove the conflicting base entry, or
use a different base). The base assets are still preserved (we
never overwrite); we just refuse to build a broken pack.

Removed the now-unused skip-summary log keys.
v0.3.5
2026-05-23 17:26:41 +09:00
9efd4a696a rp-pack: never overwrite base resourcepack sounds/paintings (v0.3.5)
If the base resourcepack already has audio files under
assets/musicquiz/sounds/ or entries in assets/musicquiz/sounds.json,
the build now PRESERVES them and skips any new track that would
collide. Same policy for painting textures: existing cover_*.png
in the base are not overwritten by new ones.

Per-track collision is logged so the user can see exactly what was
preserved and what was skipped. Summary counts (added / skipped)
are also logged.

Requested by 사금향: "기존에 있는걸 삭제하거나 이상하게 엎어쓰지
말것" — preserve base assets unconditionally.
2026-05-23 17:18:46 +09:00
c580a50fd4 installer: escape agreement tab labels (XSS hardening)
RP installer already escapes k.tab; main installer was injecting it raw.
Add escapeHtml helper and apply to tab id/label so admin-supplied
agreement labels can't break the HTML.
v0.3.4
2026-05-20 10:29:06 +09:00
38df72e4f6 terms: phrase agreement-list failure as install failure (per request)
User asked for "약관 표시 실패시 설치 실패로 처리". The block-on-failure path
is already in place; this just sharpens the message so users see "설치를
진행할 수 없습니다" rather than a soft retry prompt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 10:22:02 +09:00
6447b1cb78 terms: block install on terms list fetch failure (retry UI)
Reviewer caught that v0.3.4 was bypassing the agreement step entirely on
network/server errors, letting users install without ever seeing terms.
Now only the explicit empty-list response (terms:[]) skips the step.
Network errors, 404s, and IPC failures render an error page with Back/Retry
buttons; no next button is exposed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 10:20:49 +09:00
9ba5dc6b7b terms: per-term installer visibility toggles + universal delete (v0.3.4)
- _meta.json: customLabels -> terms.{label,showInInstaller,showInInstallerRp}
- Drop builtin protection; any term kind can be deleted/added/toggled
- New public route /manifest/terms/<pack>/index.json for installer term lists
- Installers fetch terms:list dynamically; skip agreement step if list empty
- Term editor: 2 visibility checkboxes (설치기 / 리소스팩 설치기), multi-select
- Migration from old schema preserves custom labels (default: visible in both)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 10:14:42 +09:00
05dc9d7166 terms: seed-on-fetch + rename/delete sync (v0.3.3)
- public route `/manifest/terms/:packKey/:fileName` 가 sendFile 전에
  `ensurePackTermsDir(packKey)` 를 호출하도록 수정. 관리자가 사이트 약관
  페이지를 한 번도 열지 않은 fresh 배포에서도 설치기가 정상적으로 약관을
  받을 수 있다. `loadPackDefinition` 으로 실제 pack 만 허용해 임의 키로
  빈 폴더가 생성되는 것을 차단.
- `renamePack`: pack JSON 이름이 바뀌면 `manifest/terms/<oldKey>/` 도
  `<newKey>/` 로 함께 rename.
- `deletePackKeys`: pack 삭제 시 약관 폴더도 `fs.rm` 으로 정리 — 동일 key
  재생성 시 옛 약관 부활 방지.
- `ensurePackTermsDir` export.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.3.3
2026-05-20 01:39:28 +09:00
25977d894b terms: per-pack storage + import from another pack (v0.3.2)
- store.ts: 약관을 manifest/terms/<packKey>/ 폴더별로 저장. 첫 접근 시
  legacy 전역 .md 파일을 시드로 자동 복사한다.
- importTerms() 추가: 다른 음악퀴즈의 .md + _meta.json 을 현재 pack 으로
  복사한다. 동일 kind 는 source 값으로 덮어쓴다.
- /op/agreement 라우트를 세 단계로 분리:
  · /op/agreement → 음악퀴즈 카드 선택 페이지
  · /op/agreement/:packName → 해당 pack 의 약관 목록 + 추가 + 불러오기
  · /op/agreement/:packName/:kind → 에디터
- 공개 라우트도 /manifest/terms/:packKey/:fileName 으로 변경.
- 설치기 main.ts: state.selectedKey 를 약관 URL 에 포함하도록 수정 (메인 +
  rp 양쪽). pack 미선택 상태에서는 에러 반환.
- termsEditor.js: PACK_KEY 를 받아 저장 URL 에 포함.
- 다른 음악퀴즈 후보 select + 확인 모달 + locale 추가.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.3.2
2026-05-20 01:29:04 +09:00
c14b0507c7 terms: dark editor BG, vertical row layout, add/delete custom kinds
- 약관 편집기 배경/슬래시 메뉴를 사이트 다크 팔레트로 통일 (흰 배경 + 흰 글씨 가시성 문제 해결)
- 약관 목록을 가로 풀폭 1줄씩 세로로 쌓이는 레이아웃으로 변경
- 사용자 정의 약관 추가/삭제 지원
  - manifest/terms/_meta.json 에 라벨 저장
  - builtin 5종(map/resourcepack/mod/installer/installer-rp)은 삭제 불가, "기본" 배지 표시
  - kind 식별자 규칙: 소문자/숫자/하이픈 32자 이내
  - 공개 라우트 /manifest/terms/<file>.md 는 isPublicTermsFile() 로 _meta.json 차단
- 0.3.0 → 0.3.1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.3.1
2026-05-20 01:12:10 +09:00
ffb2048627 terms: agreement pages + site Notion-style editor + rp cancel fix
- 5종 약관(map/resourcepack/mod/installer/installer-rp) markdown 시드 + manifest/terms/ 노출
- 사이트 /op/agreement 목록 + Notion 스타일 markdown 에디터 (슬래시 명령어, 미리보기)
- 메인 installer: 음악퀴즈 선택 직후 약관 동의 페이지(맵·모드·설치기) 추가
- rp installer: 음악퀴즈 선택 직후 약관 동의 페이지(리소스팩·설치기) 추가
- rp installer 취소 버그 수정: buildResourcepackZip 단계간 + archive.abort() 폴링
- rp installer 취소 UX: 즉시 "취소 중…" 표시, 취소 시 installFailed 알림 생략
- 0.2.6 → 0.3.0 (큰 기능)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.3.0
2026-05-20 00:55:36 +09:00
bc3841147f installer: write custom icon into Minecraft launcher profile
Minecraft launcher's "설치 설정" screen reads `profile.icon` from
launcher_profiles.json. We were leaving it unset, so the launcher fell
back to the default Furnace icon. Inline build/icon.png as a base64
data URL at build time (scripts/build-launcher-icon.cjs generates
src/installer/launcherIcon.ts) and set it on the profile we write.

The build/ directory isn't included in the electron-builder asar (it's
only used to point at the .ico for the exe), so a runtime read isn't
possible — the icon ships compiled into the bundle. To refresh after
changing icon.png, run `npm run build:launcher-icon` (it's wired into
`dist:win` so a fresh exe build always regenerates it).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.2.6
2026-05-18 23:56:02 +09:00
40986bee11 installer-rp: delete base resourcepack zip after composing final pack
The RP installer downloads a fresh copy of the base zip into its temp
dir and composes the final pack on top of it. The base zip the main
installer placed in .mc_custom/resourcepacks/ has nothing to do after
that — but it stays in the Minecraft resource-pack list as a second
entry. Delete it after the final zip is written.

Guard against the case where the user set outputPackName equal to the
base filename, which would make base path == final path; in that case
we leave it alone so we don't wipe the file we just wrote.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.2.5
2026-05-18 21:28:36 +09:00
bf225f51e1 installer: force fabric-installer JVM stdout to UTF-8
Korean Windows defaults the JVM's stdout to cp949 (MS949), so the
fabric-installer's Korean status lines came through as mojibake when
Node decoded them as UTF-8 (e.g. "���가져오는중 (org.ow2.asm:asm:9.9)").

Pass -Dfile.encoding/-Dstdout.encoding/-Dstderr.encoding=UTF-8 before
-jar so the JVM writes UTF-8 and our existing utf-8 decode matches.
stdout/stderr.encoding properties take effect on Java 18+;
file.encoding covers older JDKs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.2.4
2026-05-18 21:22:44 +09:00
2371af4411 installer: clean platform-cache in finally so failures don't leak
Previous version only deleted platform-cache at the very end of the
success path. If anything between platform install and launcher
profile update failed, the cache jar stuck around. Move the rm into
a finally block so the directory is always cleaned up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.2.3
2026-05-18 19:10:01 +09:00
1f59f6a98b installer: move yt-dlp/ffmpeg under .mc_custom/installer/, clean platform-cache
- yt-dlp.exe, ffmpeg.exe now live in %appdata%/.mc_custom/installer/ so
  the .mc_custom root stays a clean Minecraft game folder. Existing
  binaries at the old location are migrated on first run.
- After a successful install, the platform-cache (downloaded fabric /
  forge / neoforge installer jars) is deleted — it's regenerable and
  was just wasting disk space.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.2.2
2026-05-18 19:02:01 +09:00
794ad9b778 installer-rp: rename fallback pack name musicquiz → resourcepack
Per user request: when outputPackName is empty, fall back to
`<packKey>_resourcepack` instead of `<packKey>_musicquiz`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.2.1
2026-05-18 18:54:31 +09:00
f810719d92 installer-rp: site-configured outputPackName for built zip
Adds a new "생성되는 리소스팩 이름" admin field saved to the pack
manifest and consumed by the rp installer when naming the final zip.
Empty value falls back to <packKey>_musicquiz; Windows-invalid chars
are sanitized to '_'. Bumps version 0.1.1 → 0.2.0 (new feature).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.2.0
2026-05-18 18:34:46 +09:00
ae771668de installer-rp: ship installer/styles.css so packaged UI renders
The rp installer's `index.html` references `../installer/styles.css`,
which works in dev because both source directories sit side by side.
The packaged exe's `files` list only included `installer-rp/**`, so
inside the asar the stylesheet path resolved to nothing and the UI
rendered completely unstyled (per user screenshot).

Add the single shared file `installer/styles.css` to the rp build's
file list. The cross-directory `<link>` reference now resolves inside
the asar, and we avoid duplicating the stylesheet.

Bump to 0.1.1 — small patch-level fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.1
2026-05-18 18:14:42 +09:00
40c47fbeb3 build: bundle sharp's win32-x64 prebuilt for Windows packaging
The packaged installer-rp crashed on launch with
"Could not load the 'sharp' module using the win32-x64 runtime" because
electron-builder ran on Linux and only the Linux sharp variants were
present in node_modules.

- Add `preinstall:sharp-win32` script that force-installs
  `@img/sharp-win32-x64@0.34.5` into the local node_modules (npm refuses
  it on Linux without --force due to its os/cpu restrictions).
- Chain that script before both `dist:win` and `dist:win:rp` so future
  Windows builds always have the native prebuilt available.
- Exclude `@img/sharp-{,libvips-}linux*` from the packaged files list in
  both electron-builder configs so the unused Linux variants don't bloat
  the portable exe.

Verified `release/win-unpacked/resources/app.asar.unpacked/node_modules/
@img/sharp-win32-x64/lib/sharp-win32-x64.node` is present and that no
linux sharp variants ship inside the asar.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
v0.1.0
2026-05-18 02:14:55 +09:00
6e170646a7 env: commit .env.build with production SITE_BASE_URL
Track `.env.build` in version control so the production site domain
(`https://mc.tkrmagid.kr`) is baked into every portable exe build by
default. `.env` (server/dev secrets) stays gitignored.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 01:15:22 +09:00
3017e77479 env: merge .env + .env.build instead of stopping at first match
Reviewer noted that returning on the first found file meant a project-root
`.env.build` could shadow the dev `.env`, leaving the server without
`PORT`/`HOST`/`SESSION_SECRET`. Switch `loadEnv()` to iterate every
candidate with `override:false` so multiple files merge — first-loaded
value wins per key.

Order also reshuffled so dev's `.env` takes precedence over `.env.build`
at the project root (server settings stay alive), while in packaged
mode `resources/.env.build` still wins. Verified manually with a
temp-dir reproduction.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 01:11:15 +09:00
c8da4207fc build: separate .env.build for packaging, keep .env dev-only
The previous setup packaged the development `.env` into the installer
resources, mixing local server settings (PORT/HOST/SESSION_SECRET) with
the build-time site domain. Introduce a dedicated `.env.build`:

- electron-builder configs now copy `.env.build` (gitignored) into
  `resources/`, no longer touching the dev `.env`.
- `loadEnv()` prefers `resources/.env.build` first, falling back to
  `resources/.env` (for operators who hand-edit the packaged file),
  then `<root>/.env.build`, then `<root>/.env`.
- `.env.build.example` documents the build-only keys (SITE_BASE_URL,
  MANIFEST_URL, MUSIC_CONCURRENCY); server-side keys stay in `.env`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 01:03:22 +09:00