From 4274d1373fbf31eb041352db0133d9a6b60cae10 Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Fri, 3 Apr 2026 13:30:52 -0400 Subject: [PATCH] Add pet labeling UI overlay to recording playback Co-Authored-By: Claude Sonnet 4.6 --- vigilar/web/static/css/pet-labeling.css | 205 +++++++++++++ vigilar/web/static/js/pet-labeling.js | 377 ++++++++++++++++++++++++ vigilar/web/templates/recordings.html | 34 ++- 3 files changed, 615 insertions(+), 1 deletion(-) create mode 100644 vigilar/web/static/css/pet-labeling.css create mode 100644 vigilar/web/static/js/pet-labeling.js diff --git a/vigilar/web/static/css/pet-labeling.css b/vigilar/web/static/css/pet-labeling.css new file mode 100644 index 0000000..98bd426 --- /dev/null +++ b/vigilar/web/static/css/pet-labeling.css @@ -0,0 +1,205 @@ +/* Vigilar — Pet labeling overlay for recording playback */ + +/* Container: sits on top of the video element, sized to match it */ +.detection-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; /* pass-through by default; bboxes re-enable */ + z-index: 10; +} + +/* Individual bounding box */ +.detection-bbox { + position: absolute; + pointer-events: auto; + cursor: pointer; + box-sizing: border-box; + border: 2px solid #00d4d4; /* cyan default for animals */ + border-radius: 3px; + transition: border-color 0.15s ease, box-shadow 0.15s ease; +} + +.detection-bbox:hover { + border-color: #00ffff; + box-shadow: 0 0 0 2px rgba(0, 255, 255, 0.25); +} + +.detection-bbox.bbox-person { + border-color: #dc3545; +} + +.detection-bbox.bbox-person:hover { + border-color: #ff4d5e; + box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.25); +} + +.detection-bbox.bbox-vehicle { + border-color: #0d6efd; +} + +.detection-bbox.bbox-vehicle:hover { + border-color: #3d8bfd; + box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25); +} + +.detection-bbox.bbox-labeled { + border-style: solid; + opacity: 0.85; +} + +/* Species + confidence tag shown on top-left corner of bbox */ +.detection-bbox-label { + position: absolute; + top: -1px; + left: -1px; + padding: 1px 6px; + font-size: 0.7rem; + font-weight: 600; + line-height: 1.4; + background-color: #00d4d4; + color: #0d1117; + border-radius: 2px 0 2px 0; + white-space: nowrap; + pointer-events: none; + user-select: none; + transition: background-color 0.15s ease; +} + +.detection-bbox.bbox-person .detection-bbox-label { + background-color: #dc3545; + color: #fff; +} + +.detection-bbox.bbox-vehicle .detection-bbox-label { + background-color: #0d6efd; + color: #fff; +} + +/* "Who is this?" popup card */ +.label-popup { + position: fixed; /* positioned by JS via clientX/clientY */ + z-index: 9999; + min-width: 180px; + max-width: 240px; + background-color: #161b22; + border: 1px solid #30363d; + border-radius: 8px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6); + padding: 10px; + animation: popup-appear 0.12s ease-out; + pointer-events: auto; +} + +@keyframes popup-appear { + from { + opacity: 0; + transform: scale(0.92) translateY(-4px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.label-popup-title { + font-size: 0.75rem; + font-weight: 600; + color: #8b949e; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 8px; + padding-bottom: 6px; + border-bottom: 1px solid #30363d; +} + +.label-popup-pets { + display: flex; + flex-direction: column; + gap: 4px; + margin-bottom: 6px; +} + +.label-popup-pet-btn { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + text-align: left; + padding: 5px 8px; + border: 1px solid #30363d; + border-radius: 6px; + background: transparent; + color: #e6edf3; + font-size: 0.85rem; + cursor: pointer; + transition: background-color 0.1s ease, border-color 0.1s ease; +} + +.label-popup-pet-btn:hover { + background-color: #21262d; + border-color: #58a6ff; +} + +.label-popup-pet-btn .pet-species-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: #00d4d4; + flex-shrink: 0; +} + +.label-popup-divider { + border: none; + border-top: 1px solid #30363d; + margin: 6px 0; +} + +.label-popup-action-btn { + display: flex; + align-items: center; + gap: 6px; + width: 100%; + text-align: left; + padding: 5px 8px; + border: 1px dashed #30363d; + border-radius: 6px; + background: transparent; + color: #8b949e; + font-size: 0.8rem; + cursor: pointer; + transition: color 0.1s ease, border-color 0.1s ease; +} + +.label-popup-action-btn:hover { + color: #e6edf3; + border-color: #8b949e; +} + +/* Spinner shown while fetching pets */ +.label-popup-loading { + text-align: center; + color: #8b949e; + font-size: 0.8rem; + padding: 8px 0; +} + +/* Confirmation flash on successful label */ +.label-confirm-flash { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 3px; + background-color: rgba(0, 212, 212, 0.2); + pointer-events: none; + animation: confirm-flash 0.5s ease-out forwards; +} + +@keyframes confirm-flash { + 0% { opacity: 1; } + 100% { opacity: 0; } +} diff --git a/vigilar/web/static/js/pet-labeling.js b/vigilar/web/static/js/pet-labeling.js new file mode 100644 index 0000000..e785599 --- /dev/null +++ b/vigilar/web/static/js/pet-labeling.js @@ -0,0 +1,377 @@ +/* Vigilar — Pet labeling overlay for recording playback + * + * Integration: + * 1. Add to the page: + * + * + * + * 2. Wrap the