v3.0.5: RP2040 브라우저 에뮬레이터 — rp2040js 기반 전체 구현

- RP2040(Pico) 보드 감지·컴파일·에뮬레이션 지원 추가
  - Serial → Serial1(UART0) 자동 리다이렉트 (#define 삽입)
  - .uf2 파일 우선 추출, bootrom 자동 다운로드·캐싱
  - ELF .text 추출: arm-none-eabi-objcopy 대신 Node.js 내장 파서
  - HTTP 302 리다이렉트 자동 팔로우 (_httpsGetFollow)
- rp2040js 에뮬레이터: fast-path 초기화 루프, 피코 SVG 보드, GPIO25 LED
- 예외 처리: HardFault(#3)만 중지, 나머지 IRQ/SysTick 정상 통과
- UART onByte: TextDecoder stream 모드로 한글 UTF-8 정상 출력
- heartbeat 메시지 제거
- WiFi scan mock: WSCN 프로토콜로 C++ ↔ 브라우저 동기화, UI 편집 지원
- 에뮬레이터 팝업 → 코드 에디터 인라인 iframe 패널로 전환
- CDN 의존성 → /vendor/ 로컬 서빙 (xterm, Monaco)
- SSE 세션 idle timer (창 닫힘 후 5분 자동 종료)
- 업로드: 속도 자동 재시도(921600→460800→230400) + FlashMode fallback

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kim
2026-06-19 13:37:56 +09:00
co-authored by Claude Sonnet 4.6
parent 5a7c38b26d
commit 23831f79ba
7 changed files with 819 additions and 86 deletions
+16 -5
View File
@@ -16,13 +16,13 @@
<!-- CodeMirror + xterm (UMD libs loaded before Monaco's AMD loader to avoid anonymous define() clashes) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/css/xterm.css">
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0.10.0/lib/addon-fit.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-web-links@0.11.0/lib/addon-web-links.js"></script>
<link rel="stylesheet" href="/vendor/xterm.css">
<script src="/vendor/xterm.js"></script>
<script src="/vendor/addon-fit.js"></script>
<script src="/vendor/addon-web-links.js"></script>
<link rel="stylesheet" href="styles.css">
<!-- Monaco loader (AMD). Hide define.amd first so any stray UMD lib does not call Monaco's anonymous define(). -->
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.52.0/min/vs/loader.js"></script>
<script src="/vendor/vs/loader.js"></script>
<script>
try {
var _t = localStorage.getItem('code-page-theme') || localStorage.getItem('cherryclaw_theme') || 'dark';
@@ -510,6 +510,17 @@
</div>
<iframe id="code-preview-iframe" src="" style="flex:1;border:none;background:#fff;min-height:0" sandbox="allow-scripts allow-same-origin allow-forms allow-modals allow-popups"></iframe>
</div>
<!-- 🖥️ Emulator panel -->
<div id="code-emu-panel" style="display:none;flex-direction:column;flex-shrink:0;height:520px;min-height:120px;border-top:2px solid #1a4a25;background:#0a1410;position:relative">
<div id="code-emu-drag" style="position:absolute;top:0;left:0;right:0;height:5px;cursor:ns-resize;z-index:2;transition:background .15s"></div>
<div style="display:flex;align-items:center;height:32px;background:#0d1f18;border-bottom:1px solid #1a4a25;flex-shrink:0;padding:0 8px;gap:6px">
<span style="font-size:13px;flex-shrink:0">🖥️</span>
<span id="emu-panel-title" style="font-size:12px;font-weight:700;color:#4ade80;font-family:'IBM Plex Mono',monospace;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">에뮬레이터</span>
<button onclick="emuPanelNewTab()" title="새 탭에서 열기" style="background:none;border:none;color:#4a7a5c;cursor:pointer;font-size:13px;padding:0 5px;flex-shrink:0">⧉</button>
<button onclick="toggleEmuPanel()" title="닫기" style="background:none;border:none;color:#3d6b4f;cursor:pointer;font-size:16px;padding:0 6px;line-height:1;flex-shrink:0">&times;</button>
</div>
<iframe id="code-emu-iframe" src="" style="flex:1;border:none;min-height:0" allow="serial;usb"></iframe>
</div>
<!-- ⚡ Upload panel -->
<div id="code-upload-panel" style="display:none;flex-direction:column;flex-shrink:0;height:300px;min-height:100px;border-top:2px solid #7a4e20;background:#1e1810;position:relative">
<div id="code-upload-drag" style="position:absolute;top:0;left:0;right:0;height:5px;cursor:ns-resize;z-index:2;transition:background .15s"></div>