feat(ios): Player 런처 — Coding/Player 단일 앱 진입점 - #63
Conversation
2026-08-13 스펙(player-ios-launcher-design)의 8개 태스크 구현 계획. 런처 순수 로직 → 정적 페이지 → 빌드 스크립트 → 각 서브앱 전환 버튼 → 실기기 검증 순서. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
isNativeShell()이 true일 때만 player-hero__controls에 "Coding으로 전환" 버튼을 표시한다. 화살표 글리프는 aria-hidden으로 감춰 접근성 이름이 정확히 "Coding으로 전환"과 일치하도록 했다 (버튼 텍스트를 그대로 버튼명으로 쓰는 브리프 의도 유지).
isNativeShell()이 true일 때만 우측 컨트롤 그룹 맨 앞에 "Player로 전환" 버튼을 표시한다. Task 6(Player 헤더)과 동일하게 화살표 글리프를 aria-hidden으로 감춰 접근성 이름이 정확히 "Player로 전환"과 일치하도록 했다.
tests/build-ios-shell.test.mjs runs a real Vite build of player/ and requires player/node_modules, which the default `npm test`/`npm run qa` chain (and thus qa-gate.yml's qa job and deploy.yml's QA gate step) never installs. Rename it to .itest.mjs so it's excluded from the tests/*.test.mjs glob, add a dedicated `test:ios-shell` script to run it manually/locally, and add a timeout to its execFileSync call so a wedged build fails instead of hanging past the test's own timeout. Also add a regression assertion that the Coding/Player header "전환" buttons' target path (../index.html) still matches where the launcher lands in the built shell.
…a padding Reorder ios/launcher/index.html's inline script so both button click listeners attach unconditionally before touching localStorage. Wrap the getLastApp/badge-reading logic and the per-click setLastApp call in their own try/catch blocks so a storage read/write failure (e.g. WKWebView storage restricted or full) only skips the "마지막 사용" badge instead of leaving the picker screen with dead buttons. Also give the launcher's body padding safe-area awareness (env(safe-area-inset-*)) to match the rest of the Capacitor shell, so content doesn't sit under the device's rounded corners/home indicator.
The 네이티브 셸에서만 전환 버튼이 보인다 test set window.Capacitor and deleted it as the last line of the test body. If an earlier assertion threw, cleanup never ran and window.Capacitor leaked into the next test. Move the cleanup into the file's existing top-level afterEach, matching the pattern already used in src/App.native-shell-switch.ui.test.jsx.
USER_GUIDE.md's iOS section still described the pre-launcher flow (server-address screen auto-opening on first launch). Add a launcher subsection explaining the Coding/Player picker appears first and the "⇄ 전환" header button returns to it, and update the manual verification checklist to match. The design spec's §2/§3 still described an abandoned approach (copying into ios/App/App/public/, launcher source at public/launcher/, CAPACITOR_BUILD=1 vite build). Rewrite them to describe what was actually built: dist-ios-shell/ as the Vite output referenced by capacitor.config.json's webDir, and launcher source at ios/launcher/ (kept out of public/ specifically so it doesn't leak into Coding's own bundle). Also correct §6, which implied the iOS Player build exposes only the public-URL mode — in fact it's the same web build as browser/extension, with Local Repo / Connected Account tabs still visible but non-functional on iOS. Documentation only, no code change.
최종 리뷰에서 남은 Minor 항목 (병합 비차단, 참고용)
원한다면 후속 커밋으로 정리하겠습니다. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2e6acef465
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| await build({ | ||
| configFile: path.join(rootDir, 'player/vite.config.js'), | ||
| root: path.join(rootDir, 'player'), |
There was a problem hiding this comment.
Disable the localhost bridge in the native Player
When this Player build runs under Capacitor, its hostname is localhost; player/src/main.jsx therefore installs the HTTP local-repo bridge because shouldInstallHttpLocalRepoBridge() accepts that hostname. This makes localBridgeAvailable true, so selecting Local Repo attempts a POST to /__maestro_player/local-replay inside the static Capacitor bundle instead of showing the intended “bridge unavailable” state, and the load fails. Suppress bridge installation for native shells or require a successful health check before treating it as available.
Useful? React with 👍 / 👎.
Summary
player/와 루트 앱(src/)은 빌드 산출물 레벨에서만 결합 (소스 경계 유지)구현
ios/launcher/— 순수 로직(launcher.js) + 정적 픽커 페이지(index.html), 마지막 선택 기억scripts/build-ios-shell.mjs— coding/player 빌드 + 런처를dist-ios-shell/로 합침,capacitor.config.json의webDir갱신player/src/lib/nativeShell.js— Player 전용 네이티브 셸 감지 유틸(신규 의존성 없음)검증
tests/build-ios-shell.test.mjs가 기본npm test/qa글롭에 걸려 있어player/의존성 없는 CI qa job/GH Pages 배포를 깨뜨릴 뻔한 문제를tests/build-ios-shell.itest.mjs+ 전용npm run test:ios-shell로 분리Test plan
npm test(root, 21 files / 127 tests)cd player && npm run qa(8 tests + UI + server)npm run test:ios-shell(신규, iOS 셸 빌드 통합 테스트 — CI 기본 글롭에서는 제외됨, 로컬/수동 실행용)npm run build(GH Pages 프로덕션 빌드 영향 없음 확인)🤖 Generated with Claude Code