This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
ScreenWakeUp is a static HTML website. The core app is one self-contained file; there is no package manager, no bundler, and no runtime dependencies. The only build tooling is a Python script that generates the translated SEO landing pages and the sitemap (see Commands).
There is no test/lint suite. The single build step regenerates the non-English SEO landing pages and rewrites sitemap.xml:
python3 scripts/generate_landing_pages.py # run from repo root- Source of truth for landing-page copy is
scripts/landing_content.py(CONTENT[slug][lang]dicts). - The script writes
<lang>/<slug>/index.htmlfor the 6 non-English languages, injects an idempotent hreflang block into the hand-written English landing pages, and rebuildssitemap.xml. - It covers only the 4 landing slugs:
prevent-teams-away,caffeine-alternative,keep-screen-awake-iphone,prevent-zoom-idle. It does not touch the main app pages (index.htmlor<lang>/index.html).
To preview locally, serve the directory (e.g. python3 -m http.server) and open index.html — nothing to compile.
Single-file app pattern: index.html (~2,230 lines) combines everything:
- Inline
<style>block with CSS custom properties (design tokens) for dark/light theming - Inline
<script>block at the bottom with all application logic
Line numbers below are approximate — the file changes often, so grep for the function name rather than trusting the number.
Localization: All UI strings live in a const LANG = { en, es, pt, fr, de, ja, ru } object (~line 1036). setLang(l) (~line 1513) applies translations by updating DOM element text. The <lang>/index.html app copies (e.g. /es/index.html) are separate hand-maintained files for SEO with their own hreflang metadata — not auto-generated (the landing-page script does not touch them).
Core features and where they live (all in index.html):
- Wake Lock (
activate/deactivate, ~line 1837):navigator.wakeLock.request('screen')with a hidden<video>fallback (mkSilentVideo, ~line 1751) for browsers without the WakeLock API; re-acquires the lock onvisibilitychange. - Picture-in-Picture mini-window (
togglePiP~line 1953,pipWarmup~line 1920): a canvas-driven<video>streamed into an always-on-top PiP clock. Safari-critical detail: the PiP video must be created and warmed up before the click (on hover/focus), because callingplay()inside the click handler consumes the user-activation Safari needs to open the window. Do not move video creation back intotogglePiP. - Pomodoro (
togglePomodoro~line 2091 /tickPom~line 2114): 25-min work / 5-min break timer. - Fullscreen clock (
enterFullscreen/exitFullscreen, ~line 2139): Fullscreen API, updates a<div>with the current time. - Custom timer (
applyCustomTimer, ~line 2036): setsselectedMin/durationMs, drives the progress bar;0means run indefinitely. - Audio chime (
playChime, ~line 2065): in-browserAudioContextoscillator, played at timer expiry.
Honesty positioning: the site deliberately does not simulate mouse/keyboard input (browsers block it, and the copy says so). Do not add fake-activity / jiggler code — it contradicts the product's stated stance in the FAQ and translations.
Theming: data-theme="dark|light" attribute on <html>, persisted to localStorage key ka-theme. CSS variables in :root and [data-theme="light"] drive all colors.
Analytics: Google Analytics 4 (G-WKJ8R82KQZ) is active; events go through a track() helper. There is no ad code anywhere in the site and there must not be: the copy promises "no ads" in several places (index.html and the donation strings), so do not add ad scaffolding.
Feedback form: Posts to Formspree (https://formspree.io/f/xlgqdzqj) via fetch in the feedbackForm submit handler (present on the main page and es/pt/fr/de — not ja/ru). Success is shown only on HTTP 200; failures show the localized feedbackErr message.
index.html— the app (English / x-default).<lang>/index.html— full app translations (es, pt, fr, de, ja, ru), hand-maintained.<slug>/index.htmland<lang>/<slug>/index.html— SEO landing pages for the 4 slugs; non-English ones are generated (see Commands).about/,privacy/,terms/— static content pages.
To add or change an app UI string: update both the LANG object in the root index.html and the matching <lang>/index.html. These are not auto-generated and must be kept in sync manually. To change landing-page copy, edit scripts/landing_content.py and re-run the generator.
Static files — deploy by uploading the directory contents to any static host (Netlify, Vercel, GitHub Pages, etc.). Run the landing-page generator first if landing copy or the page set changed.
Production is Cloudflare Pages deploying the repo root on every push to main, so tooling files (CLAUDE.md, scripts/, .gitignore) are served publicly too. _headers marks /*.md and /scripts/* as noindex and de-indexes the *.pages.dev mirror; keep those rules if the site is ever moved into a dedicated output folder (the proper fix, which needs the Pages build-output setting changed).
The owner's token budget runs out fast on this project. Default to the cheap path:
- Work solo. No multi-agent workflows, fan-outs or "verification swarms" unless the owner explicitly asks for agents in that message. One careful pass with scripts plus local checks is enough for a static site.
- Never read
index.htmlor a<lang>/index.htmlwhole (150-170 KB each).grep -nthe anchor, then read ±40 lines. Same forscripts/landing_content.py(180 KB). - Edit with anchored scripts, not by hand: exact-string replacements that abort unless the anchor matches exactly once, applied to every copy at the same time. Verify with
node --checkon the extracted<script>blocks andjson.loadson every JSON-LD block, thenpython3 scripts/generate_landing_pages.pyif landings changed. - Analytics and Search Console: extract only the table you need, never the full page text; report numbers in a short table with time-on-site included.
- Reports stay short. The audit, roadmap and past readings live in the owner's memory notes and the published audit page; do not regenerate them.
- One task per session. Long sessions re-pay their whole history on every turn; the memory notes carry the state between sessions.