尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Adaptive reading for ADHD, dyslexia and cognitive fatigue. NeuroLens takes dense
text — a PDF, an EPUB, a pasted chapter — and reshapes it into something calmer
to get through: bionic fixation, adjustable rhythm and spacing, a reading mask, a
word guide, and a set of palettes designed for low vision rather than for looks.
Live at neurolens.space.
Two environment variables are required. Without them the app renders a single
screen saying so, rather than a sign-in page that cannot work:檢視完整 README ↗Adaptive reading for ADHD, dyslexia and cognitive fatigue. NeuroLens takes dense text — a PDF, an EPUB, a pasted chapter — and reshapes it into something calmer to get through: bionic fixation, adjustable rhythm and spacing, a reading mask, a word guide, and a set of palettes designed for low vision rather than for looks.
Live at neurolens.space.
npm install
cp .env.example .env.local # then fill in the two Supabase values
npm run dev # http://localhost:8080
Two environment variables are required. Without them the app renders a single screen saying so, rather than a sign-in page that cannot work:
| Variable | What it is |
|---|---|
VITE_SUPABASE_URL | https://<project-ref>.supabase.co |
VITE_SUPABASE_ANON_KEY | The project's anon key |
VITE_SITE_URL | Optional. The canonical origin, for SEO and sitemaps. |
The anon key belongs in the browser. It is designed to be public, and it is
row-level security in the database — not the secrecy of that key — that decides
who can read what. Every table is enable row level security with policies
comparing auth.uid() to user_id; see supabase/migrations/.
If those policies are ever dropped, the key becomes an open door.
npm run check:env confirms the project is reachable and the key is accepted.
Worth running when sign-in misbehaves — it exists because a one-letter typo in a
deployed environment variable (superbase.co) broke authentication in production
while every local check passed.
| Command | What it does |
|---|---|
npm run dev | Dev server on port 8080 |
npm run build | Production build |
npm test | Unit tests — Node's own runner, no framework |
npm run test:a11y | Accessibility audit in a real browser (axe + Playwright) |
npm run typecheck | tsc --noEmit |
npm run lint | ESLint |
npm run check:env | Is Supabase reachable, and is the key accepted? |
node scripts/audit-themes.mjs | Contrast check on all 15 palettes (--fix applies) |
src/
components/ UI. app-shell.tsx is the frame; reader.tsx is the reader.
lib/
store.ts Zustand. One store, all reading state.
sync/ Offline-first Supabase sync — see below.
telemetry/ Analytics and crash reports, both schema-locked.
adaptive/ The engine that adjusts pace and spacing while you read.
seo.ts Per-route metadata and structured data.
routes/ TanStack Start file routes. api/* are server handlers.
scripts/ Build steps, audits and one-off maintenance tools.
supabase/migrations/ The schema, including every RLS policy.
tests/ Browser tests. Accessibility only, on purpose.
Stack. React 19, TanStack Start/Router, Zustand, Tailwind v4, Vite 8, Supabase (Postgres + Google OAuth), deployed on Vercel.
Reading works offline and syncs when it can, so the data layer is not a thin wrapper over the database. Worth knowing before changing it:
sync/queue.ts collapses repeated writes to
the same target, so dragging a slider is one row, not two hundred.sync/identity.ts maps between them. Two books that open
with the same licence header collide under the local scheme, which is the whole
reason the second scheme exists.localStorage is namespaced per account (sync/../storage-scope.ts).
Without it, two people on one device saw each other's library. Note that
signing out does not clear it: somebody who declined to upload their books
keeps them only there.sent, gone or defer. It used to return
nothing on failure, which read as success and dequeued the write — highlights
were being destroyed silently.The privacy policy is meant to be checkable against the source rather than taken on trust, so most of its claims are enforced somewhere specific:
src/lib/analytics.ts — every event is checked against
a fixed schema and each field accepts only values from a closed list. There is
no free-text field, so a book's contents cannot be recorded even by mistake.src/lib/telemetry/errors.ts — crash messages
are rewritten before sending: quoted text, URLs, addresses and numbers are
replaced. The tests are mostly hostile
inputs, because they are the privacy claim.supabase/migrations/0002_telemetry.sql —
the telemetry tables grant insert and nothing else. Not even the app can read
them back through the API. Retention is a pg_cron job, not a promise.This is the product, not a checklist. npm run test:a11y runs axe against every
public page at WCAG 2.2 AA on each push, and scripts/audit-themes.mjs checks
all fifteen palettes arithmetically — a browser test only ever measures whichever
one happened to be loaded.
Both found real faults when first written: eight palettes whose small print sat
between 3.88:1 and 4.34:1, and a reduced-motion rule that left text at
opacity: 0 until an observer fired, so a reader who asked for less motion got
invisible paragraphs.
Three ways, and one deliberate absence.
lib/opds/) — the open standard for book
catalogues. Calibre-Web, Kavita, Komga, Standard Ebooks and many public
libraries publish one, so "connect your library" means a reader's own server.My Clippings.txt (lib/import/) — every
Kindle writes one to its own storage. Highlights are anchored into the
matching book where it is in the library, and kept as notes where it is not.Kindle, Libby and Hoopla cannot be integrated, and the reason is not effort. None publishes an API for third-party reading apps, and their catalogues are behind DRM that only their own clients may open. Anything that appeared to work would be scraping, would break on their next deploy, and would breach their terms. OPDS and the clippings file are the honest paths to the same value: the reader's own books, and the reader's own marks.
/api/opds is the one endpoint here that fetches a URL somebody typed, which
makes it the one that could become a request forger. See
lib/opds/safe-url.ts — https only, no credentials,
no private address by literal or by DNS, no redirects followed, response
capped. The tests beside it are written as attacks rather than as edge cases.
Everything in public/images/ is AI-generated — no
photographs, no real people, no stock licences, nothing to attribute. See
public/images/CREDITS.md, which also explains why
the "How it lands" section on the landing page is written in the present tense:
a generated portrait beside a past-tense claim reads as a customer testimonial,
and there are no customers being quoted.
npm install prunes @rolldown/binding-win32-x64-msvc, and the build then
fails. Restore it with npm install --no-save @rolldown/binding-win32-x64-msvc.
Do not add it to package.json — it is the wrong platform for CI.MSYS_NO_PATHCONV=1 when passing something like /auth/v1/settings.MIT.