Files
AvianVisitors/avian/frontend/index.html
T
Twarner491 2dcb6d2fc3 Frontend: verbatim apt.ts lift + restored menu drawer
Pulls the live rendered HTML/CSS/JS from the working repo:
- styles.css: 1449 lines, full live palette + chrome rules
- index.html: live <body> structure with stats sidebar, atlas with
  svg sort icons, full detail-modal (pose toggle, recordings list,
  wiki/ebird chips), about modal
- apt.js: 2760 lines of live JS verbatim, URLs rewired:
  - /api/{recent,lifelist,firstseen,timeseries,stats,species}.json
    → ../api/birdnet-api.php?action=X
  - /api/img and /api/sketch → ../api/cutout.php
  - /api/recording and /api/spectrogram → ../api/{name}.php
  - /api/menu → ../api/menu.php (new)
  - /birdnet/stream → '' (live audio is admin-only, stripped)
  - admin endpoints (/api/auth, /api/config, /api/status, /api/regen,
    /api/wiki) left to 404 silently - the live .catch() handlers
    already absorb them

Menu drawer restored. Defaults to <body class='av-local'> which CSS
toggles to show items directly without the lock screen. Forwarded
deploys switch to av-forwarded + set AV_REQUIRE_AUTH=1 in php-fpm
env + Caddy basic_auth on /avian/api/ - see avian/forwarding/.

avian/api/menu.php: returns AvianVisitors drawer items (BirdNET-Pi UI,
logs, system, github link). 401s in forwarded mode when no
Authorization header arrives.
2026-05-28 11:55:36 -07:00

193 lines
8.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>your birds</title>
<meta name="description" content="A live bird collage from your window.">
<link rel="stylesheet" href="./styles.css">
</head>
<body class="av-local">
<header class="top">
<div class="window-pick" id="winPick" role="tablist">
<i class="seg-pill" aria-hidden="true"></i>
<button data-h="1" type="button">1H</button>
<button data-h="12" type="button">12H</button>
<button data-h="24" type="button" aria-current="true">24H</button>
<button data-h="168" type="button">7D</button>
<button data-h="1000000" type="button">ALL</button>
</div>
<button class="menu-btn" id="menuBtn">menu</button>
</header>
<aside id="menu-dd" aria-hidden="true">
<div id="dd-locked">
<form class="lock-row" id="unlockForm">
<input id="lockPass" type="password" placeholder="password" autocomplete="current-password">
<button type="submit" aria-label="unlock"></button>
</form>
<p class="lock-hint" id="lockHint">enter password to unlock tools.</p>
<p class="built-by">built with <a href="https://github.com/Twarner491/AvianVisitors" target="_blank" rel="noopener">AvianVisitors</a></p>
</div>
<nav class="menu-items" id="dd-items"></nav>
</aside>
<main class="stage">
<header class="static-head">
<button type="button" class="pre" id="aboutLink">your birds</button>
<h1 id="staticTitle">Heard Recently</h1>
</header>
<div class="views" id="views">
<!-- ========== View 1 — Collage ========== -->
<section class="view" id="v0" aria-label="Bird collage">
<div class="gcollage" id="collage"></div>
</section>
<!-- ========== View 2 — Stats ========== -->
<section class="view" id="v1" aria-label="Stats">
<div class="stats-grid">
<div class="stats-timeline" id="statsTimeline">
<!-- Editorial detection timeline. Each column = one species
from the current time window. The black square's vertical
position encodes detection count; the species name + sci
name sit rotated below. Vertical hairlines separate
columns. Hard-rendered by renderStatsTimeline(). -->
</div>
<aside class="stats-side">
<div class="grp">
<h3>By Period</h3>
<small>detections, grouped by recency</small>
<ol id="statsByPeriod"></ol>
</div>
<div class="grp">
<h3>Top Species</h3>
<small id="statsTopSpecCap">most-heard, current window</small>
<ol id="statsTopSpec"></ol>
</div>
<div class="grp">
<h3>First Detections</h3>
<small>newest additions to the life list</small>
<ol id="statsFirstSeen"></ol>
</div>
</aside>
</div>
</section>
<!-- ========== View 3 — Atlas ========== -->
<section class="view" id="v2" aria-label="Atlas">
<div class="atlas-controls">
<div class="atlas-sort" id="atlasSort" role="tablist" aria-label="sort atlas">
<i class="seg-pill" aria-hidden="true"></i>
<button data-sort="count" type="button" aria-current="true" aria-label="most heard">
<svg viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
<path d="M2 12 V8"/><path d="M6 12 V5"/><path d="M10 12 V3"/>
</svg>
<span class="tip">most heard</span>
</button>
<button data-sort="recent" type="button" aria-label="most recent">
<svg viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
<circle cx="7" cy="7" r="5.2"/><path d="M7 4 V7 L9.2 8.3"/>
</svg>
<span class="tip">most recent</span>
</button>
<button data-sort="alpha" type="button" aria-label="alphabetical">
<svg viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round">
<path d="M2.4 11.5 L4 4.5 L5.6 11.5"/><path d="M2.9 9.2 H5.1"/>
<path d="M8.2 11.5 V4.5 H10.4 a1.3 1.3 0 0 1 0 2.6 H8.2"/><path d="M8.2 7.1 H10.6 a1.5 1.5 0 0 1 0 3 H8.2"/>
</svg>
<span class="tip">a → z</span>
</button>
</div>
</div>
<div class="atlas-grid" id="atlasGrid"></div>
</section>
</div>
</main>
<div id="detail-modal" aria-hidden="true" role="dialog" aria-labelledby="modalCommon">
<div class="modal-backdrop" data-close="1"></div>
<article class="modal-card">
<button class="modal-close" type="button" aria-label="Close" data-close="1">×</button>
<div class="modal-grid">
<div class="modal-img">
<img id="modalImg" src="" alt="" loading="eager">
<div class="pose-toggle" id="modalPoseToggle" role="tablist" aria-label="Pose">
<i class="seg-pill" aria-hidden="true"></i>
<button type="button" data-pose="1" aria-label="perched">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
<path d="M3.5 6.5 C 4 4, 6 3, 8 4 C 10 3.6, 11.6 4.6, 12 6.5 L 11.5 8 C 11 9.6, 9.4 10.4, 8 10.4 C 6.4 10.4, 4.8 9.6, 4.2 8 Z"/>
<circle cx="10.6" cy="5.7" r=".4" fill="currentColor"/>
<path d="M12 6.2 L 13.6 5.8"/>
<path d="M7.5 10.4 L 7.2 12.2"/>
<path d="M8.6 10.4 L 8.9 12.2"/>
<path d="M2 12.6 H 13"/>
</svg>
<span class="tip">perched</span>
</button>
<button type="button" data-pose="2" aria-label="in flight">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
<path d="M1.5 8 Q 4.5 4, 7.5 7.5 Q 11 4, 14.5 8"/>
<path d="M7.5 7.5 L 8 9.5"/>
<circle cx="8.5" cy="7.2" r=".4" fill="currentColor"/>
<path d="M8.6 7 L 10 6.6"/>
</svg>
<span class="tip">in flight</span>
</button>
</div>
</div>
<div class="modal-info">
<h2 id="modalCommon"></h2>
<p class="sci" id="modalSci"></p>
<div class="modal-stats">
<div><span class="n" id="modalAllTime"></span><span class="lbl">all time</span></div>
<div id="modalWindowStat"><span class="n" id="modalWindow"></span><span class="lbl" id="modalWindowLbl">window</span></div>
<div><span class="n" id="modalFirstSeen"></span><span class="lbl">first heard</span></div>
</div>
<p class="desc placeholder" id="modalDesc">Loading description…</p>
<div class="modal-meta">
<span class="meta-item"><span class="k">genus</span><span class="v" id="modalGenus"></span></span>
<span class="meta-item"><span class="k">rarity</span><span class="v" id="modalRarity"></span></span>
</div>
</div>
</div>
<div class="modal-recordings">
<div class="rec-head">
<h3>Recordings</h3>
<span class="rec-count" id="modalRecCount"></span>
</div>
<ol id="modalRecordings">
<li class="rec-empty">Loading recordings…</li>
</ol>
</div>
<div class="modal-actions">
<a id="modalWiki" class="chip ext" target="_blank" rel="noopener">wiki</a>
<a id="modalEbird" class="chip ext" target="_blank" rel="noopener">ebird</a>
</div>
</article>
</div>
<div id="about-modal" aria-hidden="true" role="dialog" aria-labelledby="aboutTitle">
<div class="modal-backdrop" data-close="1"></div>
<div class="about-card" role="document">
<button type="button" class="modal-close" data-close="1" aria-label="close">×</button>
<p class="about-eyebrow">your birds</p>
<h2 id="aboutTitle">The birds outside your window</h2>
<p class="about-body">A tiny microphone identifies every passing bird with Cornell's <a href="https://birdnet.cornell.edu/" target="_blank" rel="noopener">BirdNET</a>. Each species shows up as an illustration in the collage, sized by how often it's been heard.</p>
<button type="button" class="about-explore" data-close="1">explore the birds →</button>
<p class="built-by">built by <a href="https://theodore.net" target="_blank" rel="noopener">teddy</a></p>
</div>
</div>
<nav class="slider" id="slider" aria-label="View">
<i class="seg-pill" aria-hidden="true"></i>
<button type="button" data-i="0" aria-current="true">collage</button>
<button type="button" data-i="1">stats</button>
<button type="button" data-i="2">atlas</button>
</nav>
<script src="./apt.js"></script>
</body>
</html>