Spaces:
Running
Running
feat: Add modal and notification system for investigation tools and enhance README with Docker runtime
Browse files- Dockerfile +24 -0
- README.md +1 -0
- ui/static/css/noir.css +103 -0
- ui/static/js/game_logic.js +48 -4
- ui/templates/game_interface.html +16 -0
Dockerfile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Use an official lightweight Python image
|
| 2 |
+
FROM python:3.10-slim
|
| 3 |
+
|
| 4 |
+
# Prevent Python from writing .pyc files and buffering stdout
|
| 5 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
| 6 |
+
ENV PYTHONUNBUFFERED=1
|
| 7 |
+
|
| 8 |
+
# Create a working directory
|
| 9 |
+
WORKDIR /app
|
| 10 |
+
|
| 11 |
+
# Copy requirements first to leverage Docker layer caching
|
| 12 |
+
COPY requirements.txt .
|
| 13 |
+
|
| 14 |
+
# Install dependencies
|
| 15 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 16 |
+
|
| 17 |
+
# Copy the rest of the app
|
| 18 |
+
COPY . .
|
| 19 |
+
|
| 20 |
+
# Expose port (HF Spaces typically use 7860 for Gradio/Streamlit)
|
| 21 |
+
EXPOSE 7860
|
| 22 |
+
|
| 23 |
+
# Command to run the app
|
| 24 |
+
CMD ["python", "app.py"]
|
README.md
CHANGED
|
@@ -8,6 +8,7 @@ sdk_version: 6.0.1
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: gpl-3.0
|
|
|
|
| 11 |
short_description: Even AI has something to hide
|
| 12 |
tags:
|
| 13 |
- building-mcp-track-creative
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: gpl-3.0
|
| 11 |
+
runtime: docker
|
| 12 |
short_description: Even AI has something to hide
|
| 13 |
tags:
|
| 14 |
- building-mcp-track-creative
|
ui/static/css/noir.css
CHANGED
|
@@ -403,4 +403,107 @@ button.send-btn:hover {
|
|
| 403 |
|
| 404 |
@keyframes spin {
|
| 405 |
to { transform: rotate(360deg); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 406 |
}
|
|
|
|
| 403 |
|
| 404 |
@keyframes spin {
|
| 405 |
to { transform: rotate(360deg); }
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
/* --- Tool Modal --- */
|
| 409 |
+
.modal-overlay {
|
| 410 |
+
position: fixed;
|
| 411 |
+
top: 0;
|
| 412 |
+
left: 0;
|
| 413 |
+
width: 100%;
|
| 414 |
+
height: 100%;
|
| 415 |
+
background: rgba(0, 0, 0, 0.6);
|
| 416 |
+
backdrop-filter: blur(5px);
|
| 417 |
+
z-index: 200;
|
| 418 |
+
display: none;
|
| 419 |
+
align-items: center;
|
| 420 |
+
justify-content: center;
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
.modal-overlay.active {
|
| 424 |
+
display: flex;
|
| 425 |
+
}
|
| 426 |
+
|
| 427 |
+
.modal-content {
|
| 428 |
+
background: var(--paper-color);
|
| 429 |
+
padding: 20px;
|
| 430 |
+
width: 400px;
|
| 431 |
+
border: 1px solid #555;
|
| 432 |
+
box-shadow: 0 10px 30px rgba(0,0,0,0.8);
|
| 433 |
+
font-family: 'Special Elite', cursive;
|
| 434 |
+
color: var(--ink-color);
|
| 435 |
+
transform: rotate(-1deg);
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
.modal-header {
|
| 439 |
+
font-size: 1.2rem;
|
| 440 |
+
border-bottom: 2px solid var(--ink-color);
|
| 441 |
+
margin-bottom: 15px;
|
| 442 |
+
padding-bottom: 5px;
|
| 443 |
+
font-weight: bold;
|
| 444 |
+
}
|
| 445 |
+
|
| 446 |
+
.modal-input {
|
| 447 |
+
width: 100%;
|
| 448 |
+
padding: 10px;
|
| 449 |
+
margin: 10px 0 20px 0;
|
| 450 |
+
background: rgba(255,255,255,0.5);
|
| 451 |
+
border: 1px solid #888;
|
| 452 |
+
font-family: 'Courier New', monospace;
|
| 453 |
+
font-size: 1.1rem;
|
| 454 |
+
color: #000;
|
| 455 |
+
box-sizing: border-box;
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
+
.modal-actions {
|
| 459 |
+
display: flex;
|
| 460 |
+
justify-content: flex-end;
|
| 461 |
+
gap: 10px;
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
.modal-btn {
|
| 465 |
+
padding: 8px 15px;
|
| 466 |
+
border: 1px solid #333;
|
| 467 |
+
cursor: pointer;
|
| 468 |
+
font-family: 'Special Elite', cursive;
|
| 469 |
+
transition: all 0.2s;
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
.modal-btn.confirm {
|
| 473 |
+
background: var(--desk-color);
|
| 474 |
+
color: #eee;
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
.modal-btn.confirm:hover {
|
| 478 |
+
background: #444;
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
.modal-btn.cancel {
|
| 482 |
+
background: transparent;
|
| 483 |
+
color: #333;
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
.modal-btn.cancel:hover {
|
| 487 |
+
text-decoration: line-through;
|
| 488 |
+
}
|
| 489 |
+
|
| 490 |
+
/* --- Notification Toast --- */
|
| 491 |
+
#notification-toast {
|
| 492 |
+
position: fixed;
|
| 493 |
+
bottom: 30px;
|
| 494 |
+
left: 50%;
|
| 495 |
+
transform: translateX(-50%) translateY(100px);
|
| 496 |
+
background: rgba(0, 0, 0, 0.8);
|
| 497 |
+
color: white;
|
| 498 |
+
padding: 10px 20px;
|
| 499 |
+
border-radius: 5px;
|
| 500 |
+
font-family: 'Special Elite', cursive;
|
| 501 |
+
z-index: 300;
|
| 502 |
+
transition: transform 0.3s ease-out;
|
| 503 |
+
border: 1px solid #555;
|
| 504 |
+
box-shadow: 0 5px 15px rgba(0,0,0,0.5);
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
#notification-toast.show {
|
| 508 |
+
transform: translateX(-50%) translateY(0);
|
| 509 |
}
|
ui/static/js/game_logic.js
CHANGED
|
@@ -141,9 +141,20 @@ function renderSuspects() {
|
|
| 141 |
}, 500);
|
| 142 |
}
|
| 143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
function copyToClipboard(text) {
|
| 145 |
navigator.clipboard.writeText(text).then(() => {
|
| 146 |
-
|
| 147 |
}).catch(err => {
|
| 148 |
console.error('Failed to copy: ', err);
|
| 149 |
});
|
|
@@ -319,15 +330,48 @@ function makeDraggable(elmnt) {
|
|
| 319 |
|
| 320 |
// --- Tools ---
|
| 321 |
|
|
|
|
|
|
|
| 322 |
function useTool(toolName) {
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
}
|
| 327 |
}
|
| 328 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
// --- Listeners ---
|
| 330 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
document.getElementById('send-btn').addEventListener('click', sendUserMessage);
|
| 332 |
document.getElementById('chat-input').addEventListener('keypress', function (e) {
|
| 333 |
if (e.key === 'Enter' && !e.shiftKey) {
|
|
|
|
| 141 |
}, 500);
|
| 142 |
}
|
| 143 |
|
| 144 |
+
// --- Modal & Notifications ---
|
| 145 |
+
|
| 146 |
+
function showNotification(message) {
|
| 147 |
+
const toast = document.getElementById('notification-toast');
|
| 148 |
+
toast.innerText = message;
|
| 149 |
+
toast.classList.add('show');
|
| 150 |
+
setTimeout(() => {
|
| 151 |
+
toast.classList.remove('show');
|
| 152 |
+
}, 2000);
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
function copyToClipboard(text) {
|
| 156 |
navigator.clipboard.writeText(text).then(() => {
|
| 157 |
+
showNotification("COPIED: " + text);
|
| 158 |
}).catch(err => {
|
| 159 |
console.error('Failed to copy: ', err);
|
| 160 |
});
|
|
|
|
| 330 |
|
| 331 |
// --- Tools ---
|
| 332 |
|
| 333 |
+
let pendingTool = null;
|
| 334 |
+
|
| 335 |
function useTool(toolName) {
|
| 336 |
+
pendingTool = toolName;
|
| 337 |
+
const modal = document.getElementById('tool-modal');
|
| 338 |
+
const input = document.getElementById('modal-input');
|
| 339 |
+
const promptText = document.getElementById('modal-prompt-text');
|
| 340 |
+
|
| 341 |
+
// Custom prompts
|
| 342 |
+
if (toolName === 'get_location') promptText.innerText = "Enter Target Phone Number:";
|
| 343 |
+
if (toolName === 'call_alibi') promptText.innerText = "Enter Witness Phone Number:";
|
| 344 |
+
if (toolName === 'get_dna_test') promptText.innerText = "Enter Evidence ID:";
|
| 345 |
+
if (toolName === 'get_footage') promptText.innerText = "Enter Camera Location:";
|
| 346 |
+
|
| 347 |
+
input.value = '';
|
| 348 |
+
modal.classList.add('active');
|
| 349 |
+
input.focus();
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
function submitTool() {
|
| 353 |
+
const input = document.getElementById('modal-input');
|
| 354 |
+
const value = input.value.trim();
|
| 355 |
+
|
| 356 |
+
if (value && pendingTool) {
|
| 357 |
+
sendAction('use_tool', { tool: pendingTool, input: value });
|
| 358 |
+
closeModal();
|
| 359 |
}
|
| 360 |
}
|
| 361 |
|
| 362 |
+
function closeModal() {
|
| 363 |
+
document.getElementById('tool-modal').classList.remove('active');
|
| 364 |
+
pendingTool = null;
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
// --- Listeners ---
|
| 368 |
|
| 369 |
+
document.getElementById('modal-cancel').onclick = closeModal;
|
| 370 |
+
document.getElementById('modal-confirm').onclick = submitTool;
|
| 371 |
+
document.getElementById('modal-input').addEventListener('keypress', function (e) {
|
| 372 |
+
if (e.key === 'Enter') submitTool();
|
| 373 |
+
});
|
| 374 |
+
|
| 375 |
document.getElementById('send-btn').addEventListener('click', sendUserMessage);
|
| 376 |
document.getElementById('chat-input').addEventListener('keypress', function (e) {
|
| 377 |
if (e.key === 'Enter' && !e.shiftKey) {
|
ui/templates/game_interface.html
CHANGED
|
@@ -74,6 +74,22 @@
|
|
| 74 |
|
| 75 |
</div>
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
<script src="../static/js/game_logic.js"></script>
|
| 78 |
</body>
|
| 79 |
</html>
|
|
|
|
| 74 |
|
| 75 |
</div>
|
| 76 |
|
| 77 |
+
<!-- Tool Input Modal -->
|
| 78 |
+
<div id="tool-modal" class="modal-overlay">
|
| 79 |
+
<div class="modal-content">
|
| 80 |
+
<div class="modal-header">INVESTIGATION TOOL</div>
|
| 81 |
+
<div id="modal-prompt-text">Enter details:</div>
|
| 82 |
+
<input type="text" id="modal-input" class="modal-input" placeholder="...">
|
| 83 |
+
<div class="modal-actions">
|
| 84 |
+
<button id="modal-cancel" class="modal-btn cancel">CANCEL</button>
|
| 85 |
+
<button id="modal-confirm" class="modal-btn confirm">SUBMIT</button>
|
| 86 |
+
</div>
|
| 87 |
+
</div>
|
| 88 |
+
</div>
|
| 89 |
+
|
| 90 |
+
<!-- Notification Toast -->
|
| 91 |
+
<div id="notification-toast">Notification</div>
|
| 92 |
+
|
| 93 |
<script src="../static/js/game_logic.js"></script>
|
| 94 |
</body>
|
| 95 |
</html>
|