Spaces:
Running
Running
File size: 9,767 Bytes
9d97138 2de9fdc 9d97138 50e1d54 9d97138 5c1450a 9d97138 7547388 9d97138 2de9fdc 9d97138 7547388 9d97138 50e1d54 9d97138 5dd93eb 9d97138 5dd93eb 9d97138 7547388 9d97138 7547388 9d97138 5dd93eb 9d97138 7547388 9d97138 b88dc75 9d97138 5dd93eb 7547388 5dd93eb 7547388 5dd93eb 9d97138 8807cbe 9d97138 7547388 50e1d54 8807cbe 50e1d54 9d97138 50e1d54 9d97138 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | <!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Telearn Arcade Player - {{ project_display_name }}</title>
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Outfit:wght@400;600;800&family=Noto+Sans+TC:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/player.css">
</head>
<body>
<!-- Top Navigation Bar -->
<div class="top-bar">
<div class="project-info">
<a class="brand-logo" {% if student_token %}href="/student/{{ student_token }}"{% else %}href="/"{% endif %}>
<img src="/images/logo-light.png" alt="Telearn Logo" class="logo-img" />
<span class="brand-name">Telearn 得能教育</span>
</a>
<button class="btn-back" id="btn-back">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
返回大廳
</button>
<div class="project-title-wrapper" style="margin-left: 1.2rem; border-left: 1px solid rgba(255, 255, 255, 0.08); padding-left: 1.2rem;">
<span class="project-path">{{ class_name }} / {{ student_name }}</span>
<span class="project-name">{{ project_display_name }}</span>
</div>
</div>
<div class="status-wrapper status-connecting" id="server-status">
<div class="status-dot"></div>
<span id="status-text">連線中...</span>
</div>
</div>
<!-- Main Content Grid -->
<div class="main-container" id="player-layout">
<!-- 1. Screen Viewport -->
<div class="viewport-section">
<div class="loading-overlay" id="loading-overlay" style="display:none;">
<div class="loading-spinner"></div>
<p id="loading-text">正在啟動專案執行器…</p>
<p class="loading-sub" id="loading-sub">MediaPipe / 模型載入可能需要數秒</p>
</div>
<div class="webcam-denied" id="webcam-denied" style="display:none;">
<p><strong>無法使用攝影機</strong></p>
<p id="webcam-denied-msg">請在瀏覽器允許鏡頭權限,或使用 HTTPS / localhost。</p>
</div>
<div class="screen-frame" id="viewport-frame">
<!-- Will be dynamically populated with either <img> or <iframe> -->
</div>
<div class="focus-prompt" id="focus-prompt-el" style="display: none;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
<span>點選畫面啟用鍵盤/滑鼠控制</span>
</div>
</div>
<!-- 2. Side Panel -->
<div class="control-panel" id="side-panel">
<div class="device-section" id="python-device-section">
<h3 class="device-title">虛擬硬體設定</h3>
<button class="btn-webcam" id="btn-toggle-webcam">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
啟用 Webcam 影像串流
</button>
<div class="action-buttons">
<button class="btn-action btn-restart" id="btn-restart">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67"/></svg>
重設
</button>
<button class="btn-action btn-stop" id="btn-stop">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/></svg>
終止
</button>
</div>
</div>
<div class="terminal-section" id="python-terminal-section">
<div class="terminal-header-bar">
<h3 class="device-title">終端機日誌</h3>
<button class="btn-clear" id="btn-clear-console">清除</button>
</div>
<div class="terminal-box" id="console-logs"></div>
<div class="terminal-input-wrapper">
<span class="terminal-prompt">></span>
<input type="text" id="terminal-input-field" autocomplete="off" />
</div>
</div>
<!-- APK Info Section -->
<div id="apk-info-section" style="display: none; padding: 1.5rem; flex-direction: column; gap: 1.5rem; overflow-y: auto; height: 100%;">
<div style="display: flex; flex-direction: column; gap: 1rem;">
<h3 class="device-title">Android APK 專案</h3>
<div class="apk-download-card">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#ef8068" stroke-width="1.5">
<path d="M12 2v16m0 0l-4-4m4 4l4-4M4 22h16" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<div class="apk-info-text">
<div class="apk-file-name" id="apk-file-label">app.apk</div>
<div class="apk-file-size" id="apk-size-label">大小估算中...</div>
</div>
<a id="btn-apk-download" href="#" class="btn-launch btn-launch-apk" style="text-decoration: none; font-size: 0.85rem;" download>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M12 5v14M5 12h14"/></svg>
下載 APK 檔案
</a>
</div>
</div>
<div class="qr-section">
<h3 class="device-title">掃描二維碼安裝</h3>
<div class="qr-code-wrapper">
<div id="apk-qr-image"></div>
</div>
<span class="qr-tip">使用 Android 裝置掃描此條碼,即可直接下載並安裝此應用程式</span>
</div>
<div class="guide-section">
<h3 class="device-title">安裝與模擬說明</h3>
<div class="guide-item">
<div class="guide-step-number">1</div>
<p class="guide-text">下載 APK 檔案後,在裝置上點擊安裝。若系統提示「安全性警告」,請允許瀏覽器「安裝未知來源的應用程式」。</p>
</div>
<div class="guide-item" style="border-top: 1px dashed var(--border-color); padding-top: 0.8rem; margin-top: 0.8rem;">
<div class="guide-step-number">2</div>
<p class="guide-text">
<strong>啟用網頁模擬器</strong>:<br>
本平台支援整合 <a href="https://appetize.io" target="_blank" class="guide-link">Appetize.io</a>。將您的 APK 上傳至該網站後,在專案目錄下建立 <code>appetize.txt</code> 寫入 Public Key,即可在此頁面直接操作模擬器。
</p>
</div>
</div>
</div>
</div>
</div>
<div class="idle-dialog-backdrop" id="idle-dialog" role="dialog" aria-modal="true" aria-labelledby="idle-dialog-title" hidden>
<div class="idle-dialog-card">
<div class="idle-dialog-icon" aria-hidden="true">⏳</div>
<h2 id="idle-dialog-title">還在觀看嗎?</h2>
<p>這個作品已一段時間沒有操作。為了讓其他家長也能使用展示名額,系統將在 <strong id="idle-countdown">2:00</strong> 後自動結束。</p>
<button type="button" id="btn-continue-session">繼續觀看</button>
</div>
</div>
<!-- Hidden audio/video capturing items -->
<video id="webcam-video" autoplay playsinline width="640" height="480"></video>
<canvas id="webcam-canvas" width="640" height="480"></canvas>
<script nonce="{{ request.state.csp_nonce }}">
</script>
<script src="/static/js/player.js"></script>
</body>
</html>
|