Spaces:
Running
Running
File size: 5,776 Bytes
02d9f37 93085bb 02d9f37 93085bb 02d9f37 93085bb 895e15e 02d9f37 93085bb 02d9f37 895e15e 02d9f37 895e15e 02d9f37 93085bb 02d9f37 | 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 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>YOLO Webcam + Chat</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body class="bg-dark text-white">
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h4>Welcome, {{ username }}</h4>
<a href="/logout" class="btn btn-outline-light btn-sm">Logout</a>
</div>
<div class="row">
<!-- Left Column - Webcam -->
<div class="col-md-8 mb-4">
<div class="text-center">
<h5>Live Webcam</h5>
<img src="{{ url_for('video_feed') }}" class="img-fluid rounded" style="max-height: 600px; width: 100%;">
</div>
</div>
<!-- Right Column - Highlights and Chat -->
<div class="col-md-4 mb-4">
<div class="sticky-top" style="top: 20px;">
<!-- Biome Display -->
<div class="mb-3 p-2 bg-info bg-opacity-25 rounded text-center">
<strong>Current Biome:</strong> <span id="current-biome">Loading...</span>
</div>
<!-- Detection Timeline -->
<div class="mb-4">
<h5>Detection Highlights</h5>
<div id="categorized-timeline" class="bg-secondary rounded p-3" style="max-height: 300px; overflow-y: auto;">
<p class="text-muted">Loading detection events...</p>
</div>
</div>
<!-- Group Chat - Moved here -->
<div class="mb-4">
<h5>Group Chat</h5>
<div id="chatbox" class="bg-secondary rounded p-3 mb-3" style="height: 200px; overflow-y: scroll;"></div>
<div class="input-group">
<input id="message" class="form-control" placeholder="Type a message...">
<button class="btn btn-primary" onclick="sendMessage()">Send</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// === Chat Functions ===
function loadChat() {
$.get("/chat", function(data) {
let html = "";
data.forEach(msg => {
html += `<p><strong>${msg.user}</strong>: ${msg.message}<br><small class="text-muted">${msg.timestamp}</small></p>`;
});
$("#chatbox").html(html).scrollTop(9999);
});
}
function sendMessage() {
$.post("/chat", {message: $("#message").val()}, function() {
$("#message").val("");
loadChat();
});
}
// === Detection Timeline (Categorized) ===
function loadTimeline() {
$.get("/detections", function(response) {
// Update current biome
const biome = response.current_biome || "Unknown";
// Format biome name: "biome-meadows" β "Meadows"
const formattedBiome = biome
.replace(/^biome-/, '')
.replace(/-/g, ' ')
.replace(/\b\w/g, l => l.toUpperCase());
$("#current-biome").text(formattedBiome);
// Render categorized events
const grouped = response.events || {};
const categoryTitles = {
"status": "Active Status Effects",
"recent_buff": "Recent Consumables",
"boss_ability": "Boss Powers",
"boss_health": "Boss Health",
"creature": "Creatures Nearby",
"crafting": "Crafting Stations",
"plant": "Plants & Crops",
"storage": "Storage",
"vehicle": "Vehicles",
"world": "World Events",
"other": "Other"
};
const renderOrder = [
"status", "recent_buff", "boss_ability", "boss_health",
"creature", "crafting", "plant", "storage", "vehicle", "world", "other"
];
let html = "";
let hasContent = false;
renderOrder.forEach(cat => {
const events = grouped[cat];
if (events && events.length > 0) {
hasContent = true;
const title = categoryTitles[cat] || cat.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
html += `<h6 class="mt-3">${title}</h6><div class="ps-2">`;
events.forEach(e => {
const conf = (e.confidence * 100).toFixed(0);
let badgeColor = "secondary";
if (cat === "status") badgeColor = "warning";
else if (cat === "boss_health") badgeColor = "danger";
else if (cat === "creature") badgeColor = "success";
else if (cat === "recent_buff") badgeColor = "primary";
else if (cat === "boss_ability") badgeColor = "dark";
else if (cat === "world") badgeColor = "info";
html += `<p class="mb-1"><span class="badge bg-${badgeColor}">${e.class}</span> ${conf}%</p>`;
});
html += `</div>`;
}
});
$("#categorized-timeline").html(
hasContent ? html : "<p class='text-muted'>No active detections.</p>"
);
}).fail(() => {
$("#current-biome").text("Offline");
$("#categorized-timeline").html("<p class='text-muted'>Failed to load detections.</p>");
});
}
// === Polling ===
setInterval(loadChat, 3000);
setInterval(loadTimeline, 2000);
// Initial loads
loadChat();
loadTimeline();
</script>
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
</body>
</html> |