尚未检测到更新。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.