尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证A personal air quality index: learns your triggers from your own breathing logs and shows what those logs expect from air like today's, on a four-level scale from 1 (Easy) to 4 (Dangerous).
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
A personal air quality PWA. Live at breathingindex.com.
Twenty-two real entries from my own diary, in Hamden, on the New Haven monitor. The dashed line on each row is my easy level rather than the EPA's, because there are enough logs now to have one. Ozone came out a trigger; dry air and the pollens have so far not.查看完整 README ↗A personal air quality PWA. Live at breathingindex.com.
I’ve always struggled to correlate the published air quality index with my personal ability to breathe. It’s no fun being gaslit by a “moderate” AQI of 70 that some days is fine to breathe and somedays is cripplingly bad. The AQI is doesn't take into account how the combination of pollutants affects breathing, but your lungs sure do! So this app helps you create your own BQI (Breathing Quality Index) by recording how you feel about your breathing alongside a vector of all available measures, helping you map how your lungs are affected by different pollutants like ozone, smoke, pollen, dust, mold, etc, many of which are lumped into broad-based categories by AQI that, again, your lungs don't consider whilst breathing.
Instead of a uselessly granular 1-500 AQI index, your BQI has four ratings:
Each log entry is a 1–4 rating plus the full exposure vector at log time. A good entry is tolerance evidence for everything in that entry's measured air; a bad entry with several elevated variables produces an ambiguous candidate set, which the model keeps as-is until later entries confirm one candidate or exonerate another. The full model, with worked examples, is in docs/trigger-model.md; product spec and milestones are in SPEC.md.
Official composite indices (US AQI, EU EAQI) are captured on each log entry for possible later comparison, but never appear in the UI and are never used by inference.
TypeScript, React, Vite, TanStack Router (file-based routes), vite-plugin-pwa. The inference engine is a pure module at src/engine/ with no UI dependencies; its contract is the fixture suite at tests/fixtures/trigger-cases.json (16 cases, 39 assertions, run with vitest).
source: 'airnow'; otherwise they stay a comparison strip and the model drives.dry_spore_index, a count of the five weather conditions that put dry-weather spores in the air (specs/28-mold.md).All fetching is client-side; there is no backend. Logs live in localStorage — export and import are in Settings. Pollen keeps a small history there too: the measured feed serves today forward, and grass is graded over the trailing three days (docs/trigger-model.md), so the app writes each day down as it passes — capped, per coarse grid cell, and never the forecast days that arrive with it — instead of asking for a past nobody publishes. Smoke keeps the same kind of memory for the same reason, by the hour rather than the day.
Freshness comes from the payload's own newest hour rather than from when the response arrived: the service worker serves Open-Meteo network-first with a six-hour cache, so a cache hit is indistinguishable from a live fetch at arrival time. With no readings at all the quick log still works — the rating saves at once and picks up the exposure vector for its hour from Open-Meteo's three-day history on the next successful fetch. Until it does, the entry stays out of inference.
Temperatures display in °F for browsers whose locale resolves to a Fahrenheit region and °C everywhere else, overridable in Settings. Stored exposures are always metric, so the setting relabels the display and never rewrites logged history.
The palette follows prefers-color-scheme, with no manual toggle: a 4 AM breathing check should not be a flashlight to the face, and the four severity colours are re-pointed rather than inverted so the quiet end of the ramp stays quiet on a dark ground. Every screenshot in this README is the dark palette at phone size; Settings and the first-run screen are in docs/img/ too.
Analytics is Mixpanel, lazy-loaded, pseudonymous (a device ID, not an account) and content-free: events record that a screen was viewed or an entry saved, never what was rated, tagged, noted, or measured, and IP geolocation is off. Turn it off entirely in Settings. Fonts are self-hosted, so the app makes no third-party request for chrome either.
public/privacy.html and public/terms.html are prerendered documents served straight off disk at /privacy and /terms — no bundle, no router, and excluded from the service worker's navigation fallback. The privacy page enumerates every event and property the app sends; it is a contract, so any change to a track() call site belongs in the same commit as the change to that page.
npm install
npm run dev # local dev server
npm test # breakpoint-derivation check + engine fixture suite (vitest)
npm run build # vite build + typecheck
Deploys to GitHub Pages via Actions on every push to main. To redeploy without a new commit — or when an Actions incident swallows the push trigger — dispatch it manually:
gh workflow run deploy.yml