SecureMLAPI / templates /demo.html
yenslife's picture
feat: improve demo model selection workflow
0066f5e
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Presence Detection Demo</title>
<style>
:root {
--panel-bg: #dbdbdb;
--box-bg: #bcd1e5;
--box-bg-strong: #b6cbe0;
--line: #6f6f6f;
--text: #4f4f4f;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background: #f2f2f2;
color: var(--text);
font-family: "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", sans-serif;
}
.panel {
width: min(92vw, 680px);
min-height: 520px;
background: var(--panel-bg);
border: 2px solid var(--line);
padding: 52px 38px;
display: flex;
flex-direction: column;
gap: 34px;
}
.top-row {
display: grid;
grid-template-columns: minmax(220px, 1fr) 1fr;
gap: 34px;
align-items: center;
}
.upload-wrap {
display: flex;
flex-direction: column;
gap: 10px;
}
.field-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.field-label {
font-size: 14px;
color: #555;
letter-spacing: 0.2px;
}
.model-select {
width: 100%;
padding: 11px 12px;
border: 2px solid var(--line);
background: #edf2f7;
color: #333;
font-size: 15px;
}
.upload-box {
width: 100%;
min-height: 280px;
border: 2px solid var(--line);
background: var(--box-bg);
color: #111;
display: flex;
align-items: center;
justify-content: center;
padding: 14px;
text-align: center;
cursor: pointer;
position: relative;
font-size: 22px;
}
.upload-box img {
width: min(100%, 280px);
height: 280px;
min-width: 220px;
min-height: 220px;
max-width: 100%;
max-height: 320px;
object-fit: contain;
background: #fff;
}
.upload-hint {
font-size: 13px;
color: #666;
text-align: center;
}
.stats {
font-size: clamp(20px, 2.6vw, 30px);
line-height: 1.35;
letter-spacing: 0.2px;
}
.stats .error {
margin-top: 14px;
color: #a22;
font-size: 14px;
line-height: 1.4;
word-break: break-word;
}
.actions {
margin-top: 6px;
display: grid;
place-items: center;
gap: 14px;
}
.result-pill {
width: min(100%, 320px);
height: 88px;
border: 2px solid var(--line);
background: var(--box-bg-strong);
display: grid;
place-items: center;
font-size: clamp(22px, 4.5vw, 36px);
color: #4c4c4c;
}
.submit-btn {
width: min(100%, 320px);
padding: 12px 16px;
border: 2px solid var(--line);
background: #e9edf1;
color: #333;
font-size: 16px;
cursor: pointer;
}
.submit-btn:hover {
background: #f1f4f7;
}
.file-input {
display: none;
}
@media (max-width: 640px) {
.panel {
min-height: auto;
padding: 24px 18px;
gap: 22px;
}
.top-row {
grid-template-columns: 1fr;
gap: 18px;
}
.upload-box {
min-height: 220px;
font-size: 18px;
}
.upload-box img {
width: min(100%, 220px);
height: 220px;
min-width: 160px;
min-height: 160px;
}
.stats {
font-size: 22px;
}
}
</style>
</head>
<body>
<form class="panel" action="/demo" method="post" enctype="multipart/form-data">
<input type="hidden" name="existing_image_data_url" id="existingImageDataUrl" value="{{ image_data_url or '' }}">
<div class="top-row">
<div class="upload-wrap">
<div class="field-group">
<label class="field-label" for="modelSelect">推論模型</label>
<select class="model-select" id="modelSelect" name="model_name">
{% for model in model_options %}
<option value="{{ model.name }}" {% if model.name == selected_model %}selected{% endif %}>
{{ model.name }} ({{ model.backend }})
</option>
{% endfor %}
</select>
</div>
<label class="upload-box" for="fileInput" id="uploadBox">
{% if image_data_url %}
<img src="{{ image_data_url }}" alt="Input image preview" id="previewImage">
{% else %}
<span id="placeholderText">Input Image</span>
<img src="" alt="Input image preview" id="previewImage" style="display:none;">
{% endif %}
</label>
<input class="file-input" id="fileInput" type="file" name="file" accept="image/*">
<div class="upload-hint">點擊圖片區塊選擇檔案</div>
</div>
<div class="stats">
<div>Model: {{ selected_model }}</div>
<div>Class: {{ class_label }}</div>
<div>Confidence: {{ confidence }}</div>
<div>Acc: {{ acc }}</div>
{% if error %}
<div class="error">{{ error }}</div>
{% endif %}
</div>
</div>
<div class="actions">
<div class="result-pill">{{ result_label_zh }}</div>
<button class="submit-btn" type="submit">開始預測</button>
</div>
</form>
<script>
const form = document.querySelector(".panel");
const fileInput = document.getElementById("fileInput");
const previewImage = document.getElementById("previewImage");
const placeholderText = document.getElementById("placeholderText");
const existingImageDataUrl = document.getElementById("existingImageDataUrl");
fileInput.addEventListener("change", (event) => {
const file = event.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => {
if (placeholderText) placeholderText.style.display = "none";
previewImage.src = reader.result;
previewImage.style.display = "block";
existingImageDataUrl.value = reader.result;
};
reader.readAsDataURL(file);
});
form.addEventListener("submit", (event) => {
const hasNewFile = Boolean(fileInput.files?.length);
const hasExistingImage = Boolean(existingImageDataUrl.value);
if (!hasNewFile && !hasExistingImage) {
event.preventDefault();
alert("請先上傳圖片。");
}
});
</script>
</body>
</html>