Documentation menu
Pages
Every top-level HTML page in web/, what it's for, and any wiring specific to it. All pages share the same generated head tail and footer (see the shared-partials skill) and are boot()-ed by the same app.js entry point, with per-page behaviour guarded by element presence so a page with no matching DOM is an inert no-op for that feature.
On this page
index.html (/)#
The drop-and-analyse app - the whole point of the site. Structure:
- A boot splash (
#splash) that fades out onceapp.js'sboot()runs, with a failsafe reveal if the module graph is slow. - The "quickdrop" zone: two layouts coexist in the markup, toggled by the
quickdrop--heroclass on the<section>- a three-card grid (photoDrop/audioDrop/videoDrop, each with its ownacceptfilter) or a single "any file" hero dropzone (heroDrop) plus aCompareCTA linking to/compare. Both stay wired inboot(). - Mic recording (
audioRecord/heroRecord) and live spectrogram (audioLive/heroLive) buttons alongside the audio/hero zones. - The format-info overlay (
#fmtOverlay), generated fromformats.js's catalog byformat-overlay.js- searchable, category-chip filtered. - Three result sections (
#photo,#audio,#video), each with asection-metaaside (preview/histogram/OCR slots for photo, a preview slot for video) and ananr-resultscontainer filled by the matching renderer. A generic#unknownResultscontainer above them catches every non-media kind. #formatGuideCta- a post-analysis link into that extension's/formats/<ext>page, filled and revealed byhandleFile().- The footer's "Recently analysed" panel (localStorage history) and the shared offline-tiers/dependencies footer block.
<script src="assets/js/core/navigate.js">(classic script, runs before the module graph) then<script type="module" src="assets/js/core/app.js">, plus inline service-worker registration with auto-reload-once oncontrollerchange.- Rich SEO: Open Graph/Twitter meta, two JSON-LD blocks (
WebApplication+WebSitewith aSearchAction), andmodulepreloadhints for the hot-path core modules (deliberately excluding the long-tail renderers, which stay lazy).
about.html (/about)#
A single scrollable page of sections (each <section class="section" id="..."> with an #anchor), covering: #why (rationale), #where (offline/local-first), #what (#aboutFormats - the full format catalog table, generated by format-overlay.js's renderAboutFormats()), #how (usage instructions), #faq. Also carries its own copy of the format overlay (#fmtOverlay) so format search works here too.
compare.html (/compare)#
See "The /compare page" in the repo root CLAUDE.md for the full design. In brief: two "any file" dropzones (#cmpDropA/#cmpDropB), each wired by wireCompareZone() in boot() (app.js:983) to capture a File directly - not through the normal handleFile pipeline. Once both are set, handleCompare() (app.js:1011) runs; the actual merge logic lives in renderers/compare.js (see cross-cutting.md). The page-level drag/drop overlay is suppressed here: onComparePage() (app.js:1261, detected via document.getElementById('cmpDropA')) makes the global dragenter/dragover handlers skip showing #pageDrop, so only the two named zones light up on drag, and a stray drop outside them is swallowed rather than routed into the single-file pipeline (this page has no #photo/#audio/#video sections for it to land in). Results render into a shared #unknownResults container.
stats.html (/stats)#
Public analytics page. #statsRoot holds total files-analysed and visitors counts, a per-extension breakdown table (.stats-table / #statsExtBody, with a Show all toggle), a trend chart (#statsTrendsChart with mode buttons), and a leaderboard (#statsScoresList). All populated by setupStatsPage() (web/assets/js/core/stats-page.js, imported and called near the end of boot() - app.js:1668), which reads the Worker's /api/stats endpoint. See worker.md.
patch.html (/patch)#
The public changelog: one .patch-entry per commit (auto-generated content, not hand-authored per this doc's scope - see save.bat's version-bump flow in tooling.md). Also carries a "tl;dr" release-group digest toggle, wired by setupPatchTldr() (web/assets/js/core/patch-tldr.js, called unconditionally in boot() at app.js:1739 - a no-op on every other page).
privacy.html (/privacy)#
Three sections: "Your files never leave your device" (the core promise), "What the two counters do" (explaining the visit/analysed-file stats - see worker.md), and "The small print, kept small" (the actual policy text). Static content page; no special JS wiring beyond the shared boot/footer.
atari.html (/atari)#
An unlisted Asteroids easter-egg page reusing the stats page's markup pattern (#statsRoot, #statsScores for its own leaderboard). An #atariPlay button, guarded by presence and a _wired flag against double-binding on SPA re-navigation (app.js:1673-1679), lazily imports ../games/asteroids.js and calls launchAsteroids() only when clicked - the game code (web/assets/js/games/) never loads on any other page. A hidden #atariReset dev-only button clears cache and reloads.
test.html (/test)#
CLAUDE.md documents this as a deployed-but-unlisted (noindex, unlinked) style-guide reference page - every design token, font, animation, button, control, chip, card, message, popup, loader and viewer overlay the site uses, with an in-page theme toggle, its tokens/animations generated from analyser.css by tools/prerender-testpage.mjs. As of this writing the file does not exist in the working tree (verified: no web/test.html on disk) - could not confirm its current contents from source. Per project memory, this page is intentionally left untouched and not kept in sync with new UI components regardless of what CLAUDE.md's authoring guidance says.