尚未偵測到更新。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