Commit Graph

76 Commits

Author SHA1 Message Date
9f9cffffeb feat(installer-rp): auto-start install with progress card grid
2단계 페이지 진입 즉시 설치를 시작하고, 음악·사진을 1번부터 카드 그리드로
한눈에 볼 수 있게 만든다. 다운로드는 % 게이지로, 완료/실패는 색상으로 표시.

- main: prep/item/package phase 의 ProgressEvent 를 renderer 로 송신
- music.ts: yt-dlp stdout 의 [download] X% 라인을 파싱해 onProgress 호출
- preload: onProgress 채널 구독 함수 노출
- renderer: 다음 버튼 제거, prep chip + music/image 카드 그리드 + 빌드 상태
- styles: progressCard / prepChip / progressGrid 스타일 추가

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 20:01:15 +09:00
d5079125cb fix(installer-rp): type archiver warning handler explicitly
archive.on('warning', ...) 콜백의 err 파라미터에 implicit any 가 떠서
strict tsc 빌드가 깨졌다. Error & { code?: string } 로 명시.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 19:51:33 +09:00
4b83d95cbf Resolve pack_format from the pack's mcVersion
The previous hardcoded pack_format 34 + supported_formats 34..75
covered 1.21 through 1.21.11 only, so a pack generated for the
current latest (26.1.2 → format 84) was rejected as outdated.

Add src/installer-rp/packFormat.ts with a 1.21 → 26.2 lookup table
from the Minecraft wiki and resolveResourcePackFormat() that returns
{matched, format}. Unknown mcVersion falls back to the table's most
recent entry, with a log line warning the user.

Plumb mcVersion through the load → install flow:
- rp:packs:load now also fetches /manifest/<key>.json alongside
  /file/list/<key>.json and runs it through the existing
  normalizePackDefinition so the editor and the installer agree on
  the mcVersion shape. Pack manifest load failures fall back to an
  empty mcVersion (which then triggers the latest-format fallback).
- RpFetchedPack carries mcVersion; the install handler hands it to
  buildResourcepackZip.
- buildResourcepackZip drops the constant pack_format / supported_
  formats and uses the resolved format both as pack_format and as
  the {min,max} of supported_formats. Each pack is thus pinned to
  exactly the MC version it was authored for.
- The renderer's pack card now shows "마인크래프트 <version>" in
  the small line so the user can confirm before installing.

Verified locally: pack.mcmeta generated for mcVersion "1.21",
"1.21.6", "26.1.2", and the bogus "99.9.9" produce pack_format
34 / 63 / 84 / 86 (last falls back to the table tail) respectively.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 15:44:46 +09:00
8525517a87 Build resource pack zip and drop it into .minecraft
Add archiver dep (v7 — v8 dropped the function-style default export
that the @types still describe) and a new src/installer-rp/pack.ts
that assembles the resource pack tree under tempDir/resourcepack/
and zips it to %appdata%/.minecraft/resourcepacks/<key>_musicquiz.zip.

The tree matches Minecraft 1.21+ painting variant + custom sound
conventions:

  pack.mcmeta                                           pack_format 34,
                                                        supported 34..75
  assets/musicquiz/sounds.json                          stream:true per track
  assets/musicquiz/sounds/track_NN.ogg                  from tempDir/music
  assets/musicquiz/textures/painting/cover_NN.png       from tempDir/painting

Music NN.ogg is renamed to track_NN.ogg at copy time so the sound
event ids stay readable. The painting_variant JSON definitions are
intentionally NOT generated here — those live in the data pack and
are owned by /op/datapack on the website.

Wire step 2-4 of the install IPC to call buildResourcepackZip with
the now-populated music/painting temp dirs. Step 2-5 is now just a
log line since buildResourcepackZip writes directly to the final
path.

Verified by a node smoke test: tempDir of two stub ogg files plus
two 256x256 PNGs produces a valid zip with the expected entries
and UTF-8 Korean strings in pack.mcmeta description.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 15:36:58 +09:00
9e96366956 Download and normalize painting images via sharp
Add sharp dep (libvips bindings) — fastest option for the per-image
center-crop + Lanczos resize step. Pure-JS alternatives (jimp) and
spawning ffmpeg per image were both ~5-10x slower in this hot loop.

Add src/installer-rp/images.ts:
- ytIdFromUrl: extracts the video ID from watch?v=, youtu.be/, and
  /shorts|embed/ URL forms
- downloadImage: for YouTube URLs tries i.ytimg.com/vi/<id>/
  maxresdefault.jpg first, falls back to hqdefault.jpg; plain image
  URLs go through a generic HTTP/HTTPS GET that follows 302s
- normalizeToCover: center-crop to min(w,h), Lanczos resize down to
  1024x1024 when larger, never upscales, writes PNG
- coverFileName: returns cover_NN.png with zero-padded NN

Wire step 2-3 of the install handler to download + normalize each
image into <tempDir>/painting/cover_NN.png. Zip build (step 2-4)
will pick those up next.

Verified with synthetic 1200x800 and 2000x1500 buffers: small
input stays 800x800 (no upscale), large input becomes 1024x1024.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 15:30:11 +09:00
5e3a42ff4f Add ffmpeg prep and music ogg download to rp installer
Add src/installer-rp/ffmpeg.ts that downloads BtbN/FFmpeg-Builds
win64-gpl zip into %appdata%/.mc_custom/, extracts ffmpeg.exe out
of bin/, drops it at %appdata%/.mc_custom/ffmpeg.exe and verifies
with `ffmpeg -version`. Reuses existing extract-zip dep.

Add src/installer-rp/music.ts that spawns yt-dlp with
--extract-audio --audio-format vorbis --ffmpeg-location <ffmpeg.exe>
to produce <tempDir>/NN.ogg per track. Streams yt-dlp stdout to
the log channel and reports stderr on non-zero exit.

Wire both into the install IPC handler: step 2-1 now preps both
binaries, step 2-2 iterates the music list and downloads each
track. Track the currently running child process in state so the
cancel button can kill it instead of waiting for it to finish.

Image / zip / place steps remain stubbed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 15:23:01 +09:00
860c30fdfe Wire yt-dlp.exe prep into resource pack installer
Add src/installer-rp/ytdlp.ts that downloads
https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe
into %appdata%/.mc_custom/yt-dlp.exe (fixed path, Windows-only since
the installer is shipped as .exe). If the file is already there and
--version works it is reused; otherwise it is re-downloaded and
verified. The server's existing OS-aware ensureYtDlp stays intact.

The install IPC handler now calls ensureYtDlpExe() in step 2-1 and
logs the resolved path. Music / image / zip / place steps are still
stubbed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 15:18:01 +09:00
db5a1e0eac Scaffold resource pack installer entry
Add a second Electron entry under src/installer-rp/ + installer-rp/
launched by `npm run installer:rp`. It is structurally separate from
the music quiz installer (own tsconfig, own preload, own renderer),
shares the existing styles via a relative link, and exposes a
three-step UI: pick a 음악퀴즈, run the install, then a 완료 page that
can open the resourcepacks folder or quit.

The install IPC handler currently scaffolds the flow per docs/add.md
(yt-dlp prep → music download → image normalize → zip build → place
at %appdata%/.minecraft/resourcepacks/) but the actual work is still
TODO. Cancel/cleanup of %appdata%/.mc_custom/.temp/ is wired up so
that future iterations can plug each step in without rewiring.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 15:11:41 +09:00
da3a398684 Show * mark when list editor has unsaved changes
Add a red asterisk in the top-right of the dashboard header (and
prefix the document title with *) whenever the dirty flag is set,
so the user can tell at a glance that the music/image list hasn't
been saved yet. The indicator is driven by markDirty/markClean so
it follows the existing tracking.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 14:58:56 +09:00
4d18c93369 Warn on unsaved navigation in list editor
Track a dirty flag set by every state mutation (inline edit, drag,
delete, modal save, fetch playlist, clear, image-from-music, playlist
URL input) and cleared by a successful save. Intercept back-link
clicks with the existing ask() confirm modal. Use beforeunload for
tab close / refresh. Also refactor ask() so cancel paths properly
discard the pending callback instead of leaking handlers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 14:55:47 +09:00
633a895617 Handle 2D grid orientation for image list drag
The image grid wraps onto multiple rows, so picking the insertion
target by Y-axis alone meant horizontal moves within a row didn't
register. Add a 'grid' orientation that checks Y for row, then X for
column position within the row.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 14:44:12 +09:00
f27c3690e3 Use in-place source move for drag instead of placeholder + display:none
The clone-placeholder approach hid the source element with
display:none, which some browsers treat as drag cancellation. The drag
appeared to "not respond at all" to mouse press.

Switch to a simpler approach: keep the source element in the DOM and
move it directly during dragover. Apply a .dragGhost class after the
drag image is captured (via setTimeout 0) so the source becomes a
translucent dashed placeholder at the prospective drop position. drop
just compares the source's current DOM index to its original
data-index.

Also add cursor:grabbing on :active for visible press feedback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 14:39:46 +09:00
e617c71b0a Fix drag regression by gating inline edit behind double-click
The previous version had contenteditable always on for title/artist
spans, which intercepted mousedown and prevented dragstart from firing
on the row. Now the spans render as plain text, and double-click
activates contenteditable + focus + select-all. blur or Enter/Escape
exits edit mode, saves to state, and re-enables row dragging.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 14:18:01 +09:00
7ac07a58ef Stable drop-preview drag + image captions
Drag-and-drop UX rewrite:
- The old "highlight target row + margin-grow animation" approach was
  driven by per-row dragenter/dragleave events. Those fire in a noisy
  enter/leave/enter cascade as the pointer crosses sub-elements and as
  the row itself grows under the pointer, which is why the gap was
  pulsing open/closed.
- New approach: a single container-level dragover handler. On dragstart
  the source row is briefly cloned into a translucent "placeholder"
  element (dashed outline, 45% opacity, pointer-events:none) inserted in
  the source's slot; the original is then hidden (display:none) right
  after dragstart so the browser can still capture it as the drag image.
  As the cursor moves over the container we compute which sibling's
  midpoint the pointer just crossed and insertBefore the placeholder
  accordingly. The list length stays constant the whole time, so there
  is no growing/shrinking gap to fight with — what the user sees is the
  dragged item itself shown semi-transparently at the exact drop slot.
  On drop, splice the array using the placeholder's index among the
  non-source children, then re-render.
- The bindContainerDnd helper handles both lists; image grid uses
  vertical Y math (same midpoint rule as the track list since cards
  flow row-by-row in the auto-fill grid). attachDraggable now only sets
  up dragstart/dragend/contextmenu per row; no more dragenter/dragleave.

Image grid:
- Image cards now have a caption below the thumbnail. When the same
  URL appears in the music list, the music entry's title/artist are
  borrowed via captionForImage(url); otherwise "(제목 없음)" muted text.
  Layout changed from a square aspect-ratio card to a flex column:
  .imgWrap holds the square thumbnail, .cardCaption sits underneath
  with single-line title + smaller muted artist line.

CSS cleanup:
- Drop the old .dropAbove margin-grow rules and .dragOver border rule
  on .trackRow/.imageCard. Replaced with .dragPlaceholder +
  .hiddenWhileDragging.
- .imageCard no longer uses aspect-ratio on itself; aspect lives on
  .imgWrap so caption can extend the card vertically.
2026-05-12 13:51:33 +09:00
635c22c7ad Migrate tsconfig to Node16 module/moduleResolution
VS Code surfaced the TS deprecation:
  'moduleResolution=node10' is deprecated and won't work in TS 7.0.

Fix: switch the root tsconfig.json from
  module: CommonJS, moduleResolution: node
to
  module: Node16, moduleResolution: Node16

TypeScript 7 only supports node16/nodenext/bundler. node16 matches the
runtime semantics we already use (Node ≥ 16, CommonJS output via the
absence of "type": "module" in package.json), so the emit is unchanged.

Side effect of Node16 resolution: relative imports must carry the .js
extension. Added .js to every relative import across src/* (17 sites,
8 files). Bare module specifiers (express, electron, node:fs, ...) are
unaffected.

Verified:
- tsc -p tsconfig.server.json — 0 errors
- tsc -p tsconfig.installer.json — 0 errors
- node dist/server/app.js boots; /op login → 302, /op/list → 200
2026-05-12 13:41:58 +09:00
7316477e23 Inline edit, URL-driven meta refresh, drag gap animation, copy-from-music
Music list tab:
- Title + artist are now contenteditable in-place. Typing updates state on
  every input event; Enter blurs (no embedded newlines), and on Save the
  current DOM text is re-synced into state.music[i].title/.artist before
  the JSON POST so a quick-save right after typing doesn't drop the last
  keystroke. While focused, the field is highlighted with the accent
  outline and unclamped so long titles wrap. Empty cells show a muted
  placeholder via :empty::before { content: attr(data-placeholder) }.
- Edit modal "save" now POSTs the new URL to /op/list/<pack>/video-meta
  (new endpoint backed by yt-dlp --dump-json --no-playlist) and patches
  state.music[i] with the returned title/channel/durationSec. If yt-dlp
  is unavailable or the lookup fails, the modal asks the user whether to
  apply just the URL change without metadata.
- Drag-and-drop UX: replaced the simple "highlight target" feedback with a
  "gap opens above the target" animation. The hovered row gets a
  .dropAbove class that animates margin-top to 64px via CSS transition,
  visually carving out the slot the dragged item will land in. Insertion
  math is now strictly "drop before the hover target" (with the
  srcIdx<dstIdx → dstIdx-1 adjustment after the splice removal), matching
  what the gap implies. Contenteditable spans no longer hijack drag start
  — on focus the parent <li>.draggable is flipped off so the user can
  freely select text, and back on at blur.

Image list tab:
- New "음악목록에서 가져오기" button. Copies state.music[*].url into
  state.images, which (via the existing thumbUrl() helper) renders each
  song's YouTube thumbnail as the image. Behind a confirm prompt because
  it replaces the entire image list.
- Same drag gap animation (.dropAbove → margin-left 80px) applied to the
  grid cards for consistency.

Server:
- youtube.ts: add fetchVideoMeta(url) using the same ensureYtDlp() path
  (auto-installed binary under %appdata%/.mc_custom). Returns one
  YtPlaylistEntry or null.
- routes/op.ts: POST /op/list/:packName/video-meta. 400 on missing URL,
  503 NO_YTDLP if the auto-install failed, 500 on other yt-dlp errors,
  200 { ok: true, entry } otherwise.

Smoke test (Rick Astley URL) returns
title=Rick Astley - Never Gonna..., channel=Rick Astley, durationSec=213.
2026-05-12 13:38:29 +09:00
7349d4e71e Auto-install yt-dlp into %appdata%/.mc_custom on first use
The previous flow required the operator to manually install yt-dlp on the
server. Now the server downloads the correct binary for the current OS/arch
from GitHub Releases (latest) the first time a playlist fetch is requested,
caches it under %appdata%/.mc_custom (resolved per-OS), chmod +x's it on
POSIX, and skips the download on every subsequent call.

- shared/paths.ts: add getAppDataDir() + getMcCustomDir() that map %appdata%
  to ~/.config (Linux), ~/Library/Application Support (macOS), or
  process.env.APPDATA (Windows) so the path is OS-agnostic.
- server/youtube.ts: replace the old "probe PATH and bail" probeYtDlp with
  ensureYtDlp(): picks the right GitHub asset name per platform.arch
  (yt-dlp.exe / yt-dlp_macos / yt-dlp_linux / yt-dlp_linux_aarch64 /
  yt-dlp_linux_armv7l), downloads it with redirect-following https.get to
  the install path, chmods +x, then verifies with `--version` before
  reporting success. Uses an installPromise singleton so concurrent
  requests don't race the download. On any failure it cleans up the
  partial file and throws YtDlpUnavailableError with a real reason.
- docs/yt-dlp-setup.md: note that auto-install is now the default; manual
  install is only for environments where the auto-download fails.

Smoke-tested on this Linux x64 box: first call downloads to
~/.config/.mc_custom/yt-dlp_linux and reports `2026.03.17`; second call
takes ~700ms (just the version probe) and reuses the cached file.
2026-05-12 13:15:25 +09:00
a532ce5507 Fix op page UI: button underline, tab panel duplication, header spacing
- /op/dashboard: remove underline from <a class="secondaryButton"> by adding
  text-decoration:none + inline-flex centering on .primaryButton/.secondaryButton/
  .dangerButton, plus margin-left:auto on .dashboardActions so the buttons stick
  to the right side of the row even when the header is laid out as flex+wrap.
- /op/list/<pack>: fix the duplicate "save/clear + playlist URL" UI showing in
  the active tab. .tabPanel had a baseline `display: block;` that overrode the
  browser default `[hidden] { display: none }`. Add an explicit
  `.tabPanel[hidden] { display: none !important }` rule so the inactive panel
  actually hides.
- /op/list, /op/list/<pack>, /op/datapack: bump the gap between the "돌아가기"
  ghost link and the page title from 8px to 20px.
- docs/yt-dlp-setup.md: install guide (single-binary curl + pipx + apt) since
  the server doesn't have pip/pipx/yt-dlp installed. Server keeps the graceful
  "수동 입력으로 진행" fallback when yt-dlp is missing.
2026-05-12 13:11:44 +09:00
a2817c921d Add /op/list, /op/list/:pack, /op/datapack web admin + spec lock
docs/add.md
 - 사진 PNG 규격을 1024×1024 (4×4 블록 슬롯 × ×16 배율) 로 못박음
 - 짧은 변 기준 가운데 정사각 크롭 + 1024 초과 시만 축소, 미만은 native 유지

신규 라우트 (모두 requireAuth):
 - GET  /op/list                          → manifest 카드 목록
 - GET  /op/list/:pack                    → 음악목록·사진목록 탭 편집기
 - POST /op/list/:pack                    → file/list/<pack>.json 저장 (JSON)
 - POST /op/list/:pack/playlist           → yt-dlp 로 플레이리스트 펼치기
 - GET  /op/datapack                      → 음악퀴즈 선택 + 출력
 - GET  /op/datapack/:pack/generate       → 임시 포맷 mcfunction 텍스트

shared/types.ts: PackList / MusicListEntry / ImageListEntry
shared/store.ts: loadPackList, savePackList, normalizePackList
shared/paths.ts: fileListDirPath = file/list/
server/youtube.ts: yt-dlp 플레이리스트 펼치기 (--flat-playlist --dump-json),
  설치 안 됐을 때 NO_YTDLP 코드로 503.

UI:
 - views/op/list.ejs: 가로 카드 목록 + 돌아가기 버튼
 - views/op/listEditor.ejs + public/listEditor.js: 탭 전환, 드래그 정렬,
   우클릭 컨텍스트 메뉴(수정/삭제), 사진 수정 모달의 [유튜브 / 이미지] 토글,
   목록 저장·초기화·플레이리스트 불러오기 확인 팝업
 - views/op/datapack.ejs: 음악퀴즈 카드 선택 팝업 → 데이터팩 출력 + 복사
 - views/op/dashboard.ejs: 상단에 [음악목록 수정] [데이터팩 수정] 버튼
 - public/styles.css: 탭, 트랙 로우, 이미지 그리드, 컨텍스트 메뉴, 모달, 코드블록

.gitignore: conversations/ 추가.

스모크: login → /op/list 렌더, POST 저장 라운드트립 OK,
/op/datapack/.../generate 텍스트 출력 OK, 플레이리스트 fetch는 yt-dlp 미설치
환경에서 503 NO_YTDLP 메시지 정상.

Section 1 (리소스팩 설치기 EXE: yt-dlp 음악 다운로드, painting variant
텍스처 패키징, 리소스팩 zip 배치) 은 후속 커밋에서 작업.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 11:38:30 +09:00
26cc625de6 docs: 리소스팩 설치기·웹사이트 추가사항 정리
add.txt를 md로 옮기면서 비어 있던 칸을 채움:
- 이미지 다운로드 방식 (yt 썸네일 + 일반 URL)
- 리소스팩 패키징 흐름 (painting-variant.md 슬롯 규격에 맞춰 변환)
- 사진목록 UI: 반응형 그리드 카드 + 번호 배지 + 토글 입력
- /op/datapack mcfunction 출력 placeholder

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 11:22:49 +09:00
d4ef76bc7f docs: painting variant 이미지 추가 가이드
리소스팩 PNG 교체만으로 그림 갈이가 가능한
1.21+ painting variant 시스템 정리.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 10:50:35 +09:00
8574aeffa2 Track file/ test artifacts in repo
Remove file/* from .gitignore so the 음악퀴즈_test pack is reproducible
across clones. The fabric-installer.jar is the real upstream 1.1.1
(205KB); the rest are minimal stub zips/jar built locally.

  file/platforms/fabric-installer.jar          (fabric-installer 1.1.1)
  file/mods/music-quiz/music-quiz-test-mod.jar (stub fabric mod jar)
  file/resourcepacks/music-quiz.zip            (stub resourcepack)
  file/maps/music-quiz-map.zip                 (stub world)
  file/servers/music-quiz-server.zip           (server stub with eula=false)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 10:07:16 +09:00
678e886a52 Wire up 음악퀴즈_test manifest to local test artifacts
Fill empty platform.downloadUrl / modsFolder / resourcepackPath / serverPath
so the test pack drives a complete installer flow end-to-end.

Test artifacts (gitignored, must be present locally under file/):
  file/platforms/fabric-installer.jar  (real fabric installer 1.0.1)
  file/mods/music-quiz/music-quiz-test-mod.jar  (stub fabric mod)
  file/resourcepacks/music-quiz.zip  (stub resourcepack)
  file/maps/music-quiz-map.zip  (stub world)
  file/servers/music-quiz-server.zip  (stub server with eula.txt=false to exercise EULA popup)

mcVersion "26.1.2" left as user authored; not a real MC version,
so 게임 실행 단계는 실패할 수 있음 — installer flow itself runs through.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:36:30 +09:00
tkrmagid-desktop
4e7a7023cd 기본 내용 수정 2026-05-11 03:31:07 +09:00
01a34e08aa Sub-step 이전/다음 navigation + EULA popup flow
- Each 3-x and 4-x sub-step now has its own [이전][다음] action row at the bottom; 이전 returns to the previous sub-step (or previous main step on the first one) so users can move freely both directions
- 3-3 EULA: replace the inline checkbox with a modal popup. After server zip downloads, the renderer reads eula.txt via server:readEula; if absent, falls back to the live minecraft.net/en-us/eula HTML via server:fetchMinecraftEula and shows it in a sandboxed iframe
- Popup buttons: 동의 → server:acceptEula and proceed to RAM check; 비동의 / X / overlay click → "EULA 동의 실패. 다운로드를 취소합니다." and re-enable 다운로드 시작 for retry
- main.ts: stop auto-deleting eula.txt after server zip extraction so the popup can read whatever the zip provided
- 4-2 install completion now keeps a state.client.clientInstalled flag so backing into 4-2 doesn't force a re-install

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:25:10 +09:00
6c042503d6 Step 3: replace stacked sub-steps with single-slot wizard flow
Mirror the step 4 change: 3-1..3-5 now render into one #subHost slot, replacing instead of stacking when each 확인 button is clicked. The duplicated section-level "4단계로 진행" button is removed; 3-5's confirm advances straight to step 4.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 21:12:26 +09:00
2dc3bc0aed Step 4: replace stacked sub-steps with single-slot wizard flow
- renderStep4 now renders 4-1/4-2/4-3 into one #subHost slot, replacing instead of stacking when 다음 is clicked
- 4-2 shows "다음" only after install completes so success message stays visible (previously auto-advanced and the message flashed)
- Drop the duplicate section-level "5단계로" button; 4-3's confirm button advances to step 5

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 21:10:01 +09:00
44847b8a55 Switch mods to per-folder auto-discovery and resourcepack to single zip
- PackDefinition: replace mods[]/resourcepacks[] with modsFolder (string) + resourcepackPath (string); drop PackAsset
- Editor: replace dynamic add/remove lists with two single inputs; remove the now-dead JS for adding/removing rows
- Server: expose GET /file/mods/<folder>/index.json that returns the list of .jar names; folder name restricted to [a-zA-Z0-9_-]+
- Installer: fetch the listing JSON and download each jar from /file/mods/<folder>/<file>.jar; download the single resourcepack from /file/resourcepacks/<file>.zip directly into resourcepacks/

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 20:51:44 +09:00
9c4f0e8dbc Switch login to password-only and split pack zip paths
- Login form/route accepts password only; matched account row provides session userId
- PackDefinition: replace packPath with mapPath (.mc_custom/saves) and serverPath (server install dir); editor exposes two .zip fields
- Installer resolves relative platform/map/server URLs against manifest origin under /file/{platforms,maps,servers}/<name>; downloads and extracts the zips

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 20:06:32 +09:00
7a963d0409 Refine installer step 2/4-1 and bind server to 127.0.0.1 by default
Step 2 (싱글/멀티): replace auto-advance with a card selection plus a
"다음" button so the user can review their choice before moving on.

Step 4-1 (모드 플랫폼): replace the "설치 / 건너뛰기" buttons with two
cards — "권장 플랫폼 설치" and "기본 마인크래프트로 설치". Selection
only records intent; the actual platform install fires in 4-2 along
with mods/resourcepacks (already handled by installer:client install).

Server: default HOST to 127.0.0.1 instead of 0.0.0.0 so the startup
log prints a Ctrl+클릭으로 바로 열 수 있는 URL. Set HOST=0.0.0.0
externally when public exposure is needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 19:51:17 +09:00
bda79e18eb Fix installer pack fetch and simplify step 1 UI
The installer pulls each pack JSON from /manifest/<file>.json, but the
server was blocking every /manifest/ path. Add a strict whitelist
route that only serves /manifest/<a-zA-Z0-9_-name>.json files (no
directory listing, no path traversal, no other extensions); keep the
catch-all 404 for anything else under /manifest/.

Also strip the manifest URL input and "목록 새로고침" button from
installer step 1 — packs auto-load on page render, only the selectable
list remains.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:44:50 +09:00
8fd7cfaaef Build music-quiz installer and management site per spec
Implements the full spec described in README.md:

Management site (Node + TypeScript + Express + EJS):
- Public main page lists packs registered in manifest.json.
- /op login (account.json, internal-only), /op/dashboard manages packs
  with horizontal-scroll cards, add/select-and-delete flow, and the
  /op/dashboard/:packName editor (Mojang release dropdown, dynamic
  mods/resourcepacks lists, platform/RAM fields, file rename).
- Routes for /manifest.json (public) and /file/* (server pack files).
- Middleware blocks /account.json and /manifest/* directory access.

Installer (Electron):
- Five page renderer driven by IPC (preload contextBridge API):
  pack pick → single/multi → server install (path no-Korean check, JDK
  detect, file download, EULA, RAM gating, local web config editor,
  UPnP/port-forward check) → client install (.mc_custom mods +
  resourcepacks + launcher_profiles.json gameDir/javaArgs) → finish
  toggles (server folder, shortcut, server start, launcher start).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:34:27 +09:00
42a7cf3426 Add development spec to README
Spec covers the .exe installer (5 step pages), management
site (Node + Express + EJS), pack JSON schema, and
manifest.json layout. Implementation will follow this spec.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:23:30 +09:00
cd79378f3c Reset repository to README title only
Approach is changing entirely; clearing prior implementation
to start over from a clean slate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 20:41:19 +09:00
9d55819e30 Remove redundant loader installer path input in editor
Path is set by the loader upload form below, so the manual
text input was redundant. Drop the field from the editor UI
and stop overwriting the saved value on form submit so an
uploaded loader keeps its path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 21:09:55 +09:00
d03ad9d826 Patch run.bat to use installed JDK path
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 20:18:13 +09:00
4453dbd8f3 Add client apply flow and asset uploads 2026-05-08 20:03:07 +09:00
427b708277 Improve installer automation and config editor 2026-05-08 19:29:07 +09:00
5ff4e20b5e Align dashboard and installer flows with spec 2026-05-08 19:02:50 +09:00
a10ca67210 Clean project docs and sample pack 2026-05-07 23:44:03 +09:00
af6e559682 Build installer and management site from spec 2026-05-07 23:22:34 +09:00
0b061e63b7 Reset repository to README title only 2026-05-07 19:26:23 +09:00
435fa89517 Fix receiver preload path on Windows
Some checks failed
Build / release (macos-latest) (push) Has been cancelled
Build / release (ubuntu-latest) (push) Has been cancelled
Build / release (windows-latest) (push) Has been cancelled
Windows Smoke Test / windows-smoke (push) Has been cancelled
2026-05-06 05:13:48 +09:00
555a5ea099 Support vanilla profile launches
Some checks failed
Build / release (macos-latest) (push) Has been cancelled
Build / release (ubuntu-latest) (push) Has been cancelled
Build / release (windows-latest) (push) Has been cancelled
Windows Smoke Test / windows-smoke (push) Has been cancelled
2026-05-06 05:11:36 +09:00
316ce0e0dc Fix uibinder global logger collision
Some checks failed
Build / release (macos-latest) (push) Has been cancelled
Build / release (ubuntu-latest) (push) Has been cancelled
Build / release (windows-latest) (push) Has been cancelled
Windows Smoke Test / windows-smoke (push) Has been cancelled
2026-05-06 05:06:43 +09:00
a6ed744b82 Fix launcher startup bootstrap race
Some checks failed
Build / release (macos-latest) (push) Has been cancelled
Build / release (ubuntu-latest) (push) Has been cancelled
Build / release (windows-latest) (push) Has been cancelled
Windows Smoke Test / windows-smoke (push) Has been cancelled
2026-05-06 05:03:21 +09:00
20677380b9 Fix launch cache sanitization and progress layout
Some checks failed
Build / release (macos-latest) (push) Has been cancelled
Build / release (ubuntu-latest) (push) Has been cancelled
Build / release (windows-latest) (push) Has been cancelled
Windows Smoke Test / windows-smoke (push) Has been cancelled
2026-05-06 04:58:12 +09:00
e42ff9245c Sanitize legacy distributions on load
Some checks failed
Build / release (macos-latest) (push) Has been cancelled
Build / release (ubuntu-latest) (push) Has been cancelled
Build / release (windows-latest) (push) Has been cancelled
Windows Smoke Test / windows-smoke (push) Has been cancelled
2026-05-06 04:49:52 +09:00
5b96908254 Ensure distributions always include address
Some checks failed
Build / release (macos-latest) (push) Has been cancelled
Build / release (ubuntu-latest) (push) Has been cancelled
Build / release (windows-latest) (push) Has been cancelled
Windows Smoke Test / windows-smoke (push) Has been cancelled
2026-05-06 04:47:06 +09:00
9ab8d88fd5 Replace distribution JSON editor with form UI
Some checks failed
Build / release (macos-latest) (push) Has been cancelled
Build / release (ubuntu-latest) (push) Has been cancelled
Build / release (windows-latest) (push) Has been cancelled
Windows Smoke Test / windows-smoke (push) Has been cancelled
2026-05-06 03:15:30 +09:00