尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Type a message and the device says it out loud. For people whose voice is temporarily gone.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Type a message, press Enter (or the Play button) and the phone says it out
loud. Built for a phone, for people whose voice is temporarily gone.
Live at.檢視完整 README ↗Type a message, press Enter (or the Play button) and the phone says it out loud. Built for a phone, for people whose voice is temporarily gone.
Live at https://speechless.filipchalupa.cz/.
index.html, style.css and main.js are the whole app; settings.html
is the settings page and privacy.html the privacy notice. No framework,
no bundler; scripts/build.mjs only renames assets (see Deployment).
assets/ holds the icons, the link-preview picture and the screenshots,
scripts/ the build and the generators that make those pictures.main.js runs on the app page and on the settings page alike and picks
its part by whether the composer form exists. The settings page saves to
localStorage; the app reads it back when it loads again, so nothing has to
be synced between the two.translate.google.com/translate_tts). Longer text is split into chunks of
up to 180 characters and played in order through a single reused <audio>
element, which is also what keeps playback unlocked on iOS after the first
tap.Referer: Google Translate TTS answers 404 to any request
that carries one.navigator.languages that the app has (list of LANGUAGES in main.js),
or Czech when none of them is. The choice is
remembered, and every history entry keeps the language it was recorded in,
so a pinned Slovak phrase is never read out by a German voice.en, cs and sk are
translated (TRANSLATIONS in main.js); anything else falls back to
English.PRESETS in
main.js) in the device language, because an empty history means typing
from scratch in the first real situation. A language without wording of its
own gets the English one stored as English. Only a missing localStorage
key counts as a first run: a cleared history stays cleared.speechSynthesis exposes no signal at all. With reduced motion on they
still move, only slower and less: they are the one sign that sound is
coming out, the way a spinner is for loading.speechSynthesis) and says so in the status line. Offline it skips the
request entirely — on iOS the user gesture that permits speaking would
expire while waiting for a request that cannot succeed. If the device has no
voice for the language it says so rather than reading the text in a foreign
accent.manifest.webmanifest,
assets/icons/apple-touch-icon.png, service worker) and opens without a connection.
On iPhone: Safari → Share → Add to Home Screen. The manifest's id keeps
the installed app the same app if start_url ever changes, and
launch_handler brings the open window forward instead of opening a
second one.speechless:device-voice in localStorage). With it on, Play never contacts Google and goes straight
to speechSynthesis, the same path as offline. It exists for privacy, not
quality: the typed text then never leaves the phone.Every text played the normal way goes to Google as a query parameter, along
with the user's IP address, and the people this is for may well be typing in
a hospital. privacy.html says so in plain words, in English, Czech and
Slovak; main.js links to it from the app page with the interface language
in the hash, and style.css shows only that section where :has() is
supported. Its "last updated" date is filled in by the build from the last
commit that touched the file, which is why the workflow checks out the full
history. Keep the page true to the code: a new request to anyone, or a new
place data is kept, belongs on that page in the same commit.
All three pages carry Open Graph and Twitter card tags, with
an absolute og:image URL because crawlers do not resolve relative ones.
The picture is assets/og-image.png, 1200×630, generated together with the
icons (see Images).
Open index.html directly, or serve the sources — no build needed:
npx serve .
assets/icons/icon.svg is the source; every PNG under assets/ is generated
from it by scripts/make-images.mjs: the home-screen icons (iOS ignores both
SVG icons and the manifest icons when adding to the home screen, hence
apple-touch-icon.png) and og-image.png for link previews. The install flags matter: a plain npm i in a directory
without a package.json writes one, plus a lock file.
npm i --no-save --no-package-lock playwright-core
node scripts/make-images.mjs
The script inlines the SVG into a page and sizes it with CSS, because the
obvious routes both fail quietly, leaving a file of the right dimensions and
the wrong content: pointing headless Chrome at icon.svg with a smaller window
screenshots only the top-left corner of a 512-wide drawing, and wrapping it in
an <img> on a file:// page captures the bare background, since a file://
document may not load another file:// resource.
So check the pixels after regenerating, not the file names or sizes. The icons
must stay square and fully opaque — iOS rounds the corners itself — and the
colour balance should match icon.svg: roughly 82% background blue and 17%
white for the bubble. The og-image is set in whatever sans-serif the machine
has, so it renders a little differently elsewhere.
assets/screenshots/ comes from scripts/make-screenshots.mjs the same way. It opens the
source files over file://, seeds English and a few phrases into storage so
the pictures do not depend on the machine's language, and leaves the Google
TTS request hanging to hold the "Speaking…" state. The pictures serve the
README above and the install prompt: manifest.webmanifest lists them under
screenshots, by pixel size, which Chrome only shows when every side is
between 320 and 3840 px, the long side is at most 2.3× the short one, and
each form factor keeps one aspect ratio. Change the sizes in both places
together.
node scripts/make-screenshots.mjs
A push to the deploy branch runs the workflow, which calls node scripts/build.mjs
and deploys the resulting dist/ to GitHub Pages. Work lands on main without
going live; to release, move deploy to the commit that should be public:
git push origin main:deploy
``` Nothing is compiled: the build renames
`style.css` and `main.js` to `style.<hash>.css` and `main.<hash>.js`, rewrites
the references in `index.html`, `settings.html` and `privacy.html`, and stamps
those names into the service worker.
Why: Pages serves assets with `cache-control: max-age=14400`, so a CDN edge and
a phone would otherwise hold an old version for hours. `index.html` is
short-lived, so a deploy immediately points at the new file names.
The service worker precaches those hashed files, `settings.html` and
`privacy.html`, which is why those pages are part of the build id too: without
that a reworded page would never reach an installed app that stays offline. `assets/og-image.png` and
`assets/screenshots/` are copied but not precached; only crawlers and the install
dialog fetch them. Runtime caching alone is not
enough — the script is fetched before the worker is even registered, so offline
it would be missing on the first try, and a cache miss answered with
`index.html` gives the browser HTML where it expects JavaScript.
When checking a deployed version with `curl`, use `?cb=$(date +%s)` rather than
the plain URL.