Project_Demo / templates /player.html
steveyehtw's picture
Remove showcase numbering and hide MakeCode share-page student name
2de9fdc
Raw History Blame Contribute Delete
9.77 kB
<!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">&gt;</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 }}">
window.TELEARN_PLAYER = {
className: {{ class_name|tojson }},
studentName: {{ student_name|tojson }},
studentToken: {{ student_token|tojson }},
projectName: {{ project_name|tojson }},
projectType: {{ project_type|tojson }},
externalUrl: {{ external_url|tojson }},
apkName: {{ apk_name|tojson }},
apkSize: {{ apk_size|tojson }},
appetizeKey: {{ appetize_key|tojson }},
viewOnly: {{ view_only|default(false)|tojson }},
sessionTimeoutSec: {{ session_timeout_sec|default(900) }},
idleWarningSec: {{ idle_warning_sec|default(600) }},
idleTimeoutSec: {{ idle_timeout_sec|default(720) }},
heartbeatIntervalSec: {{ heartbeat_interval_sec|default(20) }},
maxConcurrentRunners: {{ max_concurrent_runners|default(3) }}
};
</script>
<script src="/static/js/player.js"></script>
</body>
</html>