diff --git a/apps/web/index.html b/apps/web/index.html
index 0fd402d..44ae8c5 100644
--- a/apps/web/index.html
+++ b/apps/web/index.html
@@ -109,15 +109,15 @@
Open it in Chrome or Edge and press Connect. Your browser talks to the treadmill over
- Bluetooth, the same way it would to a pair of headphones — set the speed, watch your
- distance, steps and calories as you walk, and keep a record of every session. Nothing
- is sent anywhere: your walks are saved on your own device and never uploaded.
+ Bluetooth, just as it would to a pair of headphones. Set the speed, watch your
+ distance, steps and calories as you walk, and keep a record of every session. Your
+ walks are saved on your own device and never uploaded.
- Most of them. Belt Control doesn't go by model name — it asks your treadmill what it
- can do when you connect, and adapts to the answer:
+ Most of them. Belt Control doesn't go by model name. When you connect, it asks your
+ treadmill what it can do and works from there:
diff --git a/apps/web/public/compatible-treadmills/index.html b/apps/web/public/compatible-treadmills/index.html
index 4a58885..e63da41 100644
--- a/apps/web/public/compatible-treadmills/index.html
+++ b/apps/web/public/compatible-treadmills/index.html
@@ -35,9 +35,9 @@ Will it work with my treadmill?
That matters more than it sounds. WalkingPads sold under the same model name have shipped
- with different parts inside in different years, and the X21 turns up in
- KingSmith's own catalogue in two different versions. A list organised by model name would
- be wrong for somebody. Asking the treadmill directly is right for everybody.
+ with different parts inside in different years, and the X21 appears in
+ KingSmith's own catalogue in two versions. A list organised by model name would be wrong
+ for someone. Asking the treadmill directly works for everyone.
So the useful question isn't what your pad is called. It's which of four Bluetooth
@@ -97,27 +97,25 @@
The four families
- A dash means the treadmill simply doesn't report that number. The app shows a dash rather
- than a figure it invented.
+ A dash means the treadmill doesn't report that number, so the app shows a dash instead of
+ making one up.
- On the last row, Belt Control can find and identify your pad but can't drive it yet. If
- that's what you get, there's something useful you can do about it — see the bottom of
- this page.
+ For pads in the last row, Belt Control can find and identify them but can't control them
+ yet. If that's yours, you can help change that — see the bottom of this page.
Why some pads have a Pause button and others don't
- Pausing isn't the same as stopping. A pause keeps your walk running in the background, so
- when you start again the treadmill picks up where you left off instead of beginning a new
- session at zero.
+ Pausing isn't the same as stopping. When you pause, the treadmill holds on to your walk,
+ so when you start again it picks up where you left off instead of starting a new session
+ from zero.
- Not every treadmill can do it, and there's no way to ask one in advance — so Belt Control
+ Not every treadmill can do it, and there's no way to ask one in advance, so Belt Control
tries it and watches what happens. If your pad turns the request down, the app stops the
- belt properly, tells you so, and hides the Pause button for the rest of the session. A
- belt still moving under a button that says Paused is the one thing most worth
- preventing.
+ belt properly, tells you so, and hides the Pause button for the rest of the session. The
+ one thing it must never do is say Paused while the belt is still moving.
Older pads in the Classic family have no pause at all — not in Belt Control, and not in
@@ -131,15 +129,15 @@
Why some calories say "rough estimate"
knowing nothing about your weight or your pace.
- Belt Control still shows that figure, but marks it and leaves it out of your weekly and
- monthly totals. Quietly adding up a number we don't trust would make every total wrong,
- which is worse than leaving a gap.
+ Belt Control still shows that figure, but marks it and leaves it out of your totals.
+ Quietly adding up a number we don't trust would make every total wrong, and that's worse
+ than leaving a gap.
Your browser matters more than your treadmill
- This is what actually stops people. Belt Control connects to your treadmill over
- Bluetooth straight from the web page — and not every browser is able to do that.
+ This is what usually trips people up. Belt Control connects to your treadmill over
+ Bluetooth straight from the web page, and not every browser can do that.
@@ -171,13 +169,14 @@
Your browser matters more than your treadmill
If your pad isn't listed
- Try it anyway. The lists above are the models we've seen, not the only ones that work —
- the app asks your treadmill directly, so an unlisted pad in a known family simply works.
+ Try it anyway. The lists above are the models we've seen, not the only ones that work.
+ The app asks your treadmill directly, so an unlisted pad from a known family should just
+ work.
If it doesn't connect, the app keeps a record of what it tried and what your treadmill
said back, and Send feedback attaches that record to an email. That
- record is the useful part: it's how a pad nobody has seen before gets supported.
+ record is what makes it possible to add support for a pad nobody has seen before.
diff --git a/apps/web/public/troubleshooting/index.html b/apps/web/public/troubleshooting/index.html
index d470623..fd85810 100644
--- a/apps/web/public/troubleshooting/index.html
+++ b/apps/web/public/troubleshooting/index.html
@@ -35,8 +35,8 @@
When the treadmill won't connect
Nothing shows up in the device list
- That list is drawn by your browser, not by this page, and it only shows what's switched
- on and nearby right now. An empty list usually means one of these:
+ That list comes from your browser, not from this page, and it only shows devices that are
+ switched on and nearby right now. An empty list usually means one of these:
The treadmill has gone to sleep. Most pads stop announcing
@@ -61,7 +61,7 @@ On a Mac, an empty list often means a permission
There's no Connect button, or pressing it does nothing
- Then your browser can't connect to Bluetooth devices. Firefox and Safari
+ That means your browser can't connect to Bluetooth devices. Firefox and Safari
can't — and on an iPhone or iPad, every browser is Safari underneath, so
installing Chrome there doesn't help. Chrome, Edge, Opera and Samsung Internet can, on a
computer or on Android.
@@ -78,9 +78,9 @@ There's no Connect button, or pressing it does nothing
It connects, then drops out after a couple of seconds
- That's certain KingSmith pads — the KS-C2 and its relatives — behaving exactly as
- designed. They hang up on anything that connects and then just listens, within about
- three seconds. It isn't a fault, and it isn't a range problem.
+ That's some KingSmith pads — the KS-C2 and its relatives — doing exactly what they were
+ designed to do. If something connects and then just listens without saying hello, they
+ hang up within about three seconds. It isn't a fault, and it isn't a range problem.
Belt Control knows the greeting those pads expect, so it shouldn't happen here. If you're
@@ -89,7 +89,7 @@
It connects, then drops out after a couple of seconds
It connects, but the belt won't start
- Common on the KS-C2, and the pad does tell us why. Four things to check, in order:
+ This is common on the KS-C2. Four things to check, in order:
The child lock is on. The pad reports this, and when it does, the
@@ -97,8 +97,8 @@ It connects, but the belt won't start
The pad is asleep. KingSmith pads go into standby after about ten
minutes idle, and in standby the motor stops responding — to the app as much as to
anything else. Bluetooth stays up, so it connects fine and then refuses every start.
- The app recognises the shape of this (the pad keeps control on its own panel and never
- sends its settings) and says so. Wake it from its panel or remote, then press Start.
+ The app can spot this (the pad keeps control on its own panel and never sends its
+ settings) and will tell you. Wake it from its panel or remote, then press Start.
Disconnecting and reconnecting does not wake it.
The treadmill's own panel still has control. After a stop, control
goes back to the pad itself, and in that state it accepts a start command and then
@@ -107,13 +107,13 @@ It connects, but the belt won't start
The pad just refuses, for no visible reason. On a real KS-C2 a start
is sometimes turned down twice and accepted on the third try, with nothing different
about the third. The app retries a refused start up to three times — only while it's
- still saying "starting", and never once you've pressed stop or disconnected. A belt
- that lurches into motion long after the app gave up, when you've already stepped off,
- is exactly what a retry must never become.
+ still saying "starting", and never once you've pressed stop or disconnected. That way
+ the belt can't lurch into motion long after the app gave up and you've stepped
+ off.
- Also worth a glance at the treadmill's own display: an error code there is a hardware
- complaint, and no app can talk it out of one.
+ It's also worth checking the treadmill's own display. An error code there means a
+ hardware problem, and no app can fix that.
The numbers look wrong
@@ -129,11 +129,11 @@
The numbers look wrong
a stalled connection.
- Wherever a number is missing you'll see a dash. An app that invents a number to avoid an
- empty space is worse than one that admits it doesn't know.
+ Wherever a number is missing, you'll see a dash. We'd rather admit we don't know than
+ invent a number to fill the gap.
-
It worked yesterday, and today my pad isn't offered
+
It worked yesterday. Why do I have to pick my pad again?
Your browser grants access to one device at a time, and only when you pick it from that
list yourself — it won't hand it over any other way. Belt Control remembers your last
@@ -148,7 +148,7 @@
It worked yesterday, and today my pad isn't offered
Still stuck? The app keeps a record of what it tried and what your
treadmill said back, and Send feedback attaches it to an email. That
- record is the useful part — it's how a pad nobody has seen before gets supported.
+ record is what makes it possible to add support for a pad nobody has seen before.
Open Belt Control
diff --git a/apps/web/public/walkingpad-bluetooth-protocol/index.html b/apps/web/public/walkingpad-bluetooth-protocol/index.html
index 6fe97b5..8630a3e 100644
--- a/apps/web/public/walkingpad-bluetooth-protocol/index.html
+++ b/apps/web/public/walkingpad-bluetooth-protocol/index.html
@@ -66,7 +66,7 @@
The four families
products, matched against advertised name prefixes. Do not copy that approach — the
mapping is not what it looks like, and the
0x1234 family was initially
missed here precisely because of an assumption about it. Detect from the GATT table
- instead. It is both more robust and shorter.
+ instead. It's sturdier, and it takes less code.
Classic WalkingPad — fe00
@@ -91,10 +91,10 @@ Classic WalkingPad — fe00
about 400 ms after the mode byte before the start.
- The leading ask stats is the part that looks like decoration and is not.
- Standby parks the same app-control path that has to be woken before anything works, so a
- start sent straight after a stop arrives at a pad that is not listening. Without it, only
- the first start of a session lands.
+ The leading ask stats looks optional, but it isn't. Standby parks the same
+ app-control path that has to be woken before anything works, so a start sent straight
+ after a stop arrives at a pad that is not listening. Without it, only the first start of
+ a session lands.
Status notifications arrive on fe01 with header F8 A2, 18 bytes,
@@ -288,10 +288,10 @@
Control, and why a start gets refused
Pause is stop, and the pad is what makes it a pause
- This was open for a long time. The official app visibly carries a pause alongside start and
+ This stayed an open question for a long time. The official app visibly carries a pause alongside start and
stop, and warns that speed cannot be adjusted while paused, but it builds its command
templates at runtime and early captures only ever exercised runState 1.
- runState 2 was the obvious guess, and a guess is not something to aim at a
+ runState 2 was the obvious guess, and a guess isn't something you send to a
treadmill.
Two independent things settled it:
@@ -361,7 +361,7 @@ The full reference
If you have a pad that lands on FitShow, or on nothing at all, the app's connection log is
- the capture that would let it be decoded properly — and it is one button to send.
+ exactly the capture needed to decode it properly, and sending it takes one button.
diff --git a/apps/web/public/walkingpad-without-the-app/index.html b/apps/web/public/walkingpad-without-the-app/index.html
index f41eaf6..715cb50 100644
--- a/apps/web/public/walkingpad-without-the-app/index.html
+++ b/apps/web/public/walkingpad-without-the-app/index.html
@@ -29,9 +29,9 @@
Using a WalkingPad without the KS+Fit app
- You don't need an account, a phone, or the manufacturer's software to run a KingSmith
- treadmill. There's no password standing between your pad and any app that knows how to
- talk to it — and a web browser can.
+ You don't need an account, a phone or the manufacturer's software to use a KingSmith
+ treadmill. Nothing locks your pad to one app: anything that speaks its language can
+ control it, and that includes a web browser.
What you actually lose without the app
@@ -42,12 +42,12 @@ What you actually lose without the app
finish.
- That memory is what the phone app is really for. The price of it is an account, a login,
- notifications, and your walking data sitting on somebody else's server. For a lot of
- people that's a bad trade, which is why this page exists.
+ That memory is what the phone app is really for. The price is an account, a login,
+ notifications, and your walking data sitting on someone else's server. For a lot of
+ people that isn't worth it, which is why this page exists.
-Your options, honestly
+Your options, compared
-How the browser one works
+How the browser option works
Modern browsers can connect to Bluetooth devices directly, once you pick the device from
- a list the browser shows you. That's all this is. There's no pairing code to type, no
- extra software in between, and nothing goes to a server — your browser talks to your
- treadmill over Bluetooth, the same way your headphones do.
+ a list the browser shows you. That's all this is: no pairing code to type, no extra
+ software in between, and nothing sent to a server. Your browser talks to your treadmill
+ over Bluetooth, the same way it talks to a pair of headphones.
In practice: open the page in Chrome or Edge, press Connect , and pick
- your pad from the list. The app then works out for itself how to talk to your particular
- treadmill. Which pads that covers.
+ your pad from the list. The app then figures out how to talk to your particular
+ treadmill. See which pads that covers.
What you get
@@ -105,8 +105,8 @@ What you get
Start, stop and set the speed, with the speed you asked for and the speed the belt is
actually doing shown separately — because sending a command isn't the same as the belt
moving.
- Live distance, steps and calories, wherever your pad reports them honestly. Where it
- doesn't, you get a dash instead of a number somebody made up.
+ Live distance, steps and calories, wherever your pad reports them reliably. Where it
+ doesn't, you get a dash instead of a made-up number.
Walks recorded automatically, with daily totals, a goal, a month of history and a
chart of how consistent you've been.
A lifetime distance counter that keeps going even when the treadmill resets its
@@ -120,10 +120,9 @@ Where your data lives
there's no account to make.
- That has a real cost worth saying plainly: clear your browsing data and your history goes
- with it, and a walk recorded on your laptop won't show up on your tablet. The backup file
- exists for exactly that reason — it's how you move your history, and it's on you to save
- one.
+ There's a real downside to that: clear your browsing data and your history goes with it,
+ and a walk recorded on your laptop won't show up on your tablet. That's what the backup
+ file is for. It's how you move your history around, and saving one is up to you.
@@ -134,27 +133,26 @@
Where your data lives
Is it safe to run a treadmill from a web page?
- It's exactly as safe as running it from the phone app — which is to say the belt can
- start with nobody standing on it, and that's true of every option on this page, remote
- included.
+ It's as safe as running it from the phone app. With every option on this page, the
+ remote included, the belt can start with nobody standing on it.
- What the app does about it: starting and resuming both ask you to confirm and show the
- speed first; the speed is capped to what your treadmill says it can do, and capped again
- in case a pad reports something nonsensical; Stop is always available, works with the
- Esc key, and stays pinned on screen from the moment you press start until the
- belt reports it has stopped. And the app never reconnects on its own, because silently
- reattaching to a treadmill that might be moving is not a safe thing to do.
+ Here's what the app does about it. Starting and resuming both ask you to confirm, and
+ show you the speed first. The speed is capped at what your treadmill says it can handle,
+ with a second cap in case a pad reports something nonsensical. Stop is always available,
+ works with the Esc key, and stays pinned on screen from the moment you press
+ Start until the belt reports it has stopped. And the app never reconnects on its own,
+ because quietly reconnecting to a treadmill that might be moving isn't safe.
- Those reduce the risk rather than removing it. Closing the page does not stop the belt.
- Use the treadmill's own controls or safety key as the real stop.
+ All of that reduces the risk, but it doesn't remove it. Closing the page does not stop
+ the belt. Treat the treadmill's own controls or safety key as the real stop.
Does it need an internet connection?
- Only to load the page the first time. After that it keeps a copy and works offline —
- the connection to your treadmill never used the internet in the first place.
+ Only to load the page the first time. After that your browser keeps a copy and it works
+ offline. The connection to your treadmill never goes over the internet anyway.
diff --git a/apps/web/src/components/AmbientView.tsx b/apps/web/src/components/AmbientView.tsx
index 697acbb..cd412af 100644
--- a/apps/web/src/components/AmbientView.tsx
+++ b/apps/web/src/components/AmbientView.tsx
@@ -83,7 +83,7 @@ export function AmbientView({ onExit }: { onExit: () => void }) {
title="Resume the belt?"
body={`The belt will pick back up at ${toMph(settings.value.targetKmh).toFixed(
1
- )} mph. Make sure it is clear and you are ready.`}
+ )} mph. Make sure it's clear and you're ready.`}
confirmLabel="Resume"
onConfirm={() => {
setConfirmResume(false);
diff --git a/apps/web/src/components/ConnectPanel.tsx b/apps/web/src/components/ConnectPanel.tsx
index ced456a..962ec22 100644
--- a/apps/web/src/components/ConnectPanel.tsx
+++ b/apps/web/src/components/ConnectPanel.tsx
@@ -42,12 +42,12 @@ export function ConnectPanel() {
return (
@@ -57,8 +57,8 @@ export function ConnectPanel() {
Try it with a simulated pad
- History still works here — you can import a backup and browse past walks
- without Bluetooth.
+ History still works here, though. You can import a backup and look through
+ past walks without Bluetooth.
);
@@ -71,7 +71,7 @@ export function ConnectPanel() {
<>
{firstRun && (
-
Drive your WalkingPad from this browser
+
Control your WalkingPad from this browser
Start, stop and set the speed over Bluetooth, without the phone app. Your walks
are saved on this device and never uploaded.
@@ -102,7 +102,7 @@ export function ConnectPanel() {
class="btn primary block lg"
onClick={() => void connect({ filtered: true })}
>
- Connect to pad
+ Connect your pad
- Pad not in the list? Some show up under a name you wouldn’t
- recognise —{' '}Show all devices shows everything nearby.
+ Don’t see your pad? Some show up under a name you wouldn’t
+ recognise, so try{' '}Show all devices to see everything nearby.
>
)}
@@ -125,8 +125,8 @@ export function ConnectPanel() {
Which treadmills work?
- Most of them. The app asks your treadmill what it can do when you connect,
- so what matters is what is inside it rather than its model name:
+ Most of them. When you connect, the app asks your treadmill what it can
+ do, so what’s inside it matters more than the name on the box:
@@ -141,7 +141,7 @@ export function ConnectPanel() {
KS-C2 and relatives — G1, MX16, K12 Pro: the same, plus pause
- Some other brands — found and named, but not driveable yet
+ Some other brands — recognised, but can’t be controlled yet
{/* The written page, not a repetition of it: every model seen on each
diff --git a/apps/web/src/components/ConnectionSheet.tsx b/apps/web/src/components/ConnectionSheet.tsx
index c5ed5b3..619a80f 100644
--- a/apps/web/src/components/ConnectionSheet.tsx
+++ b/apps/web/src/components/ConnectionSheet.tsx
@@ -162,9 +162,9 @@ export function ConnectionSheet({
)}
- Disconnecting does not stop the belt. Needs Chrome, Edge, Opera or Samsung
- Internet over HTTPS or localhost — Firefox and Safari, including on iOS, do
- not support Web Bluetooth.
+ Disconnecting doesn’t stop the belt. Works in Chrome, Edge, Opera and
+ Samsung Internet over HTTPS or localhost. Firefox and Safari (including on iPhone
+ and iPad) don’t support Web Bluetooth.
{/* Kept because the project asks people to share this when a pad speaks an
diff --git a/apps/web/src/components/DesktopOnlyNotice.tsx b/apps/web/src/components/DesktopOnlyNotice.tsx
index 9f40421..46190b7 100644
--- a/apps/web/src/components/DesktopOnlyNotice.tsx
+++ b/apps/web/src/components/DesktopOnlyNotice.tsx
@@ -34,11 +34,10 @@ export function DesktopOnlyNotice() {
Best on desktop
- Belt Control drives the treadmill over Web Bluetooth, which this app is built and
- tested for on desktop Chrome, Edge or Opera.{' '}
+ Belt Control is built and tested for Chrome, Edge and Opera on a computer.{' '}
{supported.value
- ? 'On a phone or tablet, expect a cramped layout and flaky pairing.'
- : 'This browser has no Web Bluetooth at all — Safari on iOS and Firefox never implemented it, so connecting will not work here.'}
+ ? 'On a phone or tablet the layout can feel cramped, and pairing can be unreliable.'
+ : "This browser doesn't support Web Bluetooth (Safari on iOS and Firefox never added it), so it can't connect to a treadmill."}
diff --git a/apps/web/src/components/Disclaimer.tsx b/apps/web/src/components/Disclaimer.tsx
index 3886ac2..bbaedaf 100644
--- a/apps/web/src/components/Disclaimer.tsx
+++ b/apps/web/src/components/Disclaimer.tsx
@@ -30,16 +30,16 @@ export function Disclaimer() {
return (
- Belt Control commands a motorised treadmill. Only start it when you can
- see the belt, and stop it with the treadmill's own controls or safety key rather than
- relying on this app — a Bluetooth link can drop while the belt keeps running. Provided
- without warranty; you use it at your own risk.
+ Belt Control runs a motorised treadmill. Only start it when you can see
+ the belt, and don't count on this app to stop it — use the treadmill's own controls or
+ safety key, because a Bluetooth connection can drop while the belt keeps running. No
+ warranty; you use it at your own risk.
Belt Control is an independent project. It is not affiliated with, endorsed by, or
sponsored by Beijing KingSmith Technology Co., Ltd. WalkingPad® and
- KingSmith® are trademarks of that company, used here only to say which
- treadmills this app can talk to.
+ KingSmith® are trademarks of that company, mentioned here only to say which
+ treadmills this app works with.
Safety, terms & privacy
diff --git a/apps/web/src/components/FeedbackSheet.tsx b/apps/web/src/components/FeedbackSheet.tsx
index 6b12c96..b883d19 100644
--- a/apps/web/src/components/FeedbackSheet.tsx
+++ b/apps/web/src/components/FeedbackSheet.tsx
@@ -68,8 +68,9 @@ export function FeedbackSheet({ onClose }: { onClose: () => void }) {
return (
- Goes to {SUPPORT_EMAIL}. The page sends nothing itself — it writes the mail and
- opens it in your own mail app, so you can read it, change it, or drop it.
+ This goes to {SUPPORT_EMAIL}. The page doesn’t send anything itself: it
+ drafts an email and opens it in your own mail app, so you can read it, edit it or
+ discard it first.
@@ -78,7 +79,7 @@ export function FeedbackSheet({ onClose }: { onClose: () => void }) {
class="field-input"
rows={5}
value={message}
- placeholder="What you did, what the belt did, and what you expected instead."
+ placeholder="What you did, what the belt did, and what you expected to happen."
onInput={(e) => setMessage((e.currentTarget as HTMLTextAreaElement).value)}
/>
@@ -96,7 +97,7 @@ export function FeedbackSheet({ onClose }: { onClose: () => void }) {
{lines.length > 0
? `— browser, protocol, and ${lines.length} log line${lines.length === 1 ? '' : 's'}.`
: '— browser and protocol. Nothing has been logged yet.'}{' '}
- No sessions, no history.
+ Your walk history is never included.
@@ -111,11 +112,11 @@ export function FeedbackSheet({ onClose }: { onClose: () => void }) {
{trimmed && (
{fitted.messageTruncated
- ? 'A mail link cannot carry a message this long, so the email will hold the beginning of it.'
+ ? "This message is too long for a mail link, so the email will only include the start of it."
: fitted.logShown === 0
- ? `A mail link cannot carry ${fitted.logTotal} log lines, so the email will go without them.`
- : `A mail link cannot carry ${fitted.logTotal} log lines, so the email will hold the last ${fitted.logShown}.`}{' '}
- Save the report and attach it if the rest matters.
+ ? `${fitted.logTotal} log lines won't fit in a mail link, so the email will leave them out.`
+ : `${fitted.logTotal} log lines won't fit in a mail link, so the email will include the last ${fitted.logShown}.`}{' '}
+ If you need the rest, save the report and attach it.
)}
diff --git a/apps/web/src/components/FunFact.tsx b/apps/web/src/components/FunFact.tsx
index 3549287..660b936 100644
--- a/apps/web/src/components/FunFact.tsx
+++ b/apps/web/src/components/FunFact.tsx
@@ -47,7 +47,7 @@ export function FunFact() {
return (
-
Which is to say
+
Put another way
{fmtMiles(distKm)} miles {usingDay ? 'today' : 'in total'} is {pick.text}.
diff --git a/apps/web/src/components/GoalMeter.tsx b/apps/web/src/components/GoalMeter.tsx
index 16068ee..0e626d8 100644
--- a/apps/web/src/components/GoalMeter.tsx
+++ b/apps/web/src/components/GoalMeter.tsx
@@ -76,7 +76,7 @@ function GoalDialog({ goal, onClose }: { goal: number; onClose: () => void }) {
onInput={(e) => setValue((e.target as HTMLInputElement).value)}
/>
-
Used by the meter here, the streak, and the goal line on History.
+
Your goal drives the progress meter, your streak and the goal line on History.
Cancel
diff --git a/apps/web/src/components/Recovery.tsx b/apps/web/src/components/Recovery.tsx
index 756d458..5292b1d 100644
--- a/apps/web/src/components/Recovery.tsx
+++ b/apps/web/src/components/Recovery.tsx
@@ -41,11 +41,11 @@ export function Recovery({ error, onRetry }: { error: unknown; onRetry: () => vo
return (
- Something broke
+ Something went wrong
- The app hit an error it could not recover from on its own. Nothing was sent
- anywhere, and the treadmill is unaffected — if the belt is moving, use its own
- controls or remote to stop it.
+ The app ran into an error it couldn’t recover from. Nothing was sent
+ anywhere and the treadmill wasn’t affected, but if the belt is moving, stop
+ it with the treadmill’s own controls or remote.
@@ -57,8 +57,7 @@ export function Recovery({ error, onRetry }: { error: unknown; onRetry: () => vo
Try again
- Worth one attempt — if the error came from something transient, this is all it
- takes.
+ Worth a try. If it was a one-off glitch, this is all it takes.
@@ -71,9 +70,9 @@ export function Recovery({ error, onRetry }: { error: unknown; onRetry: () => vo
Download my stored data
- The raw contents of this browser's storage, exactly as stored and without being
- interpreted — so this works even when the data is what the app is choking on.
- Keep it before clearing anything.
+ Saves everything this browser has stored for the app, exactly as it is, so it
+ works even if that data is what’s causing the problem. Save a copy before
+ you clear anything.
@@ -91,8 +90,8 @@ export function Recovery({ error, onRetry }: { error: unknown; onRetry: () => vo
Clear stored data and reload
- Deletes every walk recorded in this browser along with your goal and presets.
- It cannot be undone — download the copy above first.
+ Deletes every walk recorded in this browser, plus your goal and presets. This
+ can’t be undone, so download a copy first.
diff --git a/apps/web/src/components/StickerSheet.tsx b/apps/web/src/components/StickerSheet.tsx
index 0f18a0f..c95e224 100644
--- a/apps/web/src/components/StickerSheet.tsx
+++ b/apps/web/src/components/StickerSheet.tsx
@@ -43,7 +43,7 @@ export function StickerSheet() {
{earnedCount} of {states.length}
- {next ? `. Next is ${next.name} — ${lowerFirst(next.how)}.` : '. The sheet is full.'}
+ {next ? `. Next up: ${next.name} — ${lowerFirst(next.how)}.` : ". You've collected them all."}
>
);
diff --git a/apps/web/src/components/StreakChain.tsx b/apps/web/src/components/StreakChain.tsx
index fbbe824..7948bf8 100644
--- a/apps/web/src/components/StreakChain.tsx
+++ b/apps/web/src/components/StreakChain.tsx
@@ -57,8 +57,8 @@ export function StreakChain() {
{todayMet
- ? `${metCount} of the last ${DAYS} days met the goal.`
- : `${metCount} of the last ${DAYS} days met the goal. Meet today's to fill the last one.`}
+ ? `You hit your goal on ${metCount} of the last ${DAYS} days.`
+ : `You hit your goal on ${metCount} of the last ${DAYS} days. Hit today's to fill in the last one.`}
);
diff --git a/apps/web/src/components/Tiles.tsx b/apps/web/src/components/Tiles.tsx
index 0d7d92b..6117a0b 100644
--- a/apps/web/src/components/Tiles.tsx
+++ b/apps/web/src/components/Tiles.tsx
@@ -55,7 +55,7 @@ export function Tiles() {
{tile.v}
{tile.unverified && (
-
+
?
)}
diff --git a/apps/web/src/lib/badges.ts b/apps/web/src/lib/badges.ts
index 34095c9..49c5ad4 100644
--- a/apps/web/src/lib/badges.ts
+++ b/apps/web/src/lib/badges.ts
@@ -53,9 +53,9 @@ const WEEK = 7;
export const BADGES: readonly Badge[] = [
{ id: 'early-bird', name: 'Early Bird', how: 'Walk before 7 AM' },
{ id: 'night-owl', name: 'Night Owl', how: 'Walk after 9 PM' },
- { id: 'long-haul', name: 'Long Haul', how: 'One walk of an hour' },
+ { id: 'long-haul', name: 'Long Haul', how: 'Walk for an hour in one go' },
{ id: 'double-day', name: 'Double Day', how: 'Two walks in one day' },
- { id: 'week-straight', name: 'Week Straight', how: 'Meet the goal seven days running' },
+ { id: 'week-straight', name: 'Week Straight', how: 'Hit your goal seven days in a row' },
{ id: 'century', name: 'Century', how: 'Record a hundred walks' },
] as const;
diff --git a/apps/web/src/routes/History.tsx b/apps/web/src/routes/History.tsx
index a9f69b1..284a769 100644
--- a/apps/web/src/routes/History.tsx
+++ b/apps/web/src/routes/History.tsx
@@ -37,7 +37,7 @@ function DataCard() {
log(`backup imported: ${parts.join(', ')}`, 'ok');
trackEvent('backup_imported', { added: r.added, duplicate: r.duplicate, skipped: r.skipped });
} catch (err) {
- const text = err instanceof BackupError ? err.message : 'Could not read that file.';
+ const text = err instanceof BackupError ? err.message : "Couldn't read that file.";
setMsg({ text, err: true });
log(`backup import failed: ${text}`, 'err');
trackEvent('backup_import_failed');
@@ -78,10 +78,10 @@ function DataCard() {
)}
- {n} session{n === 1 ? '' : 's'} stored in this browser. Nothing is ever uploaded.
- The backup holds the complete record — every session, its speed samples and your
- settings — and importing one merges into what is already here rather than
- replacing it. The CSV is a flat summary for spreadsheets and cannot be read back.
+ {n} session{n === 1 ? '' : 's'} saved in this browser, and nothing is ever
+ uploaded. A backup holds everything: every session, its speed samples and your
+ settings. Importing one adds to what’s already here instead of replacing it.
+ The CSV is a simple summary for spreadsheets and can’t be imported back.
);
@@ -117,17 +117,17 @@ function LifetimeCard() {
{/* Same rule as every other total on this screen: say what is not in it. */}
{head.fellBack ? (
- Counting hours rather than miles: no walk here came from a protocol that
- reports distance on a scale this project established, and hours are measured
- by this app rather than taken from the pad.
+ Showing hours instead of miles: none of these walks came from a treadmill whose
+ distance the app can read reliably. Hours are timed by the app itself, so they
+ don’t depend on the treadmill.
) : (
t.excluded > 0 && (
{t.excluded} walk{t.excluded === 1 ? ' is' : 's are'} left out of this
- distance — recorded on a protocol whose distance scale this project had not
- established at the time, so those numbers are kept raw rather than summed as
- miles.
+ distance. {t.excluded === 1 ? 'It was' : 'They were'} recorded before the app
+ could read that treadmill’s distance reliably, so the numbers are kept
+ as they were rather than counted as miles.
)
)}
@@ -157,8 +157,8 @@ export function History() {
History
- Nothing recorded yet. Sessions start themselves when the belt moves — you do
- not have to press anything.
+ Nothing recorded yet. A session starts automatically when the belt moves, so
+ there’s nothing extra to press.
@@ -200,7 +200,7 @@ export function History() {
height={desktop ? 180 : 140}
/>
- Dashed line is the {goal}-minute goal. Filled bars met it.
+ The dashed line is your {goal}-minute goal. Filled bars are days you hit it.
{/* A table view always exists alongside the chart. */}
setShowTable((v) => !v)}>
@@ -235,19 +235,17 @@ export function History() {
{days > 0
- ? `${days}-day streak at ${goal} min or more.`
- : `No active streak. Today counts once you pass ${goal} min.`}
+ ? `${days}-day streak of ${goal} minutes or more.`
+ : `No streak right now. Walk ${goal} minutes today to start one.`}
{anyExcluded && (
- Some sessions are excluded from the distance totals above. They were recorded
- on a protocol whose distance scale this project had not established at the
- time — KingSmith 0x1234 walks logged before that pad's distance was confirmed
- to be metres. Each session keeps the trust it was recorded with, so those
- numbers stay raw and out of every aggregate rather than being converted now on
- an assumption about what they meant then.
+ Some sessions aren’t counted in the distance totals above. They’re
+ KingSmith 0x1234 walks recorded before the app confirmed that pad reports
+ distance in metres. Rather than convert them now based on a guess, each one
+ keeps its numbers exactly as recorded and stays out of the totals.
)}
diff --git a/apps/web/src/routes/Legal.tsx b/apps/web/src/routes/Legal.tsx
index 0549637..55af691 100644
--- a/apps/web/src/routes/Legal.tsx
+++ b/apps/web/src/routes/Legal.tsx
@@ -287,8 +287,7 @@ export function Legal() {
);
diff --git a/apps/web/src/routes/Now.tsx b/apps/web/src/routes/Now.tsx
index 0567e38..dd3c1ac 100644
--- a/apps/web/src/routes/Now.tsx
+++ b/apps/web/src/routes/Now.tsx
@@ -113,8 +113,8 @@ export function Now({ onAmbient }: { onAmbient: () => void }) {
End walk
- Paused — the belt is stopped. The walk stays open for 15 minutes, so
- resuming keeps it as one session rather than two.
+ Paused. The belt has stopped, and your walk stays open for 15 minutes,
+ so resuming in that time keeps it as one session, not two.
>
) : (
@@ -177,12 +177,12 @@ export function Now({ onAmbient }: { onAmbient: () => void }) {
note is no longer true of it. */}
{stopPending.value ? (
- Stop command sent — waiting for the belt to report zero.
+ Stop sent — waiting for the belt to confirm it has stopped.
) : (
startPending.value && (
- Start command sent — waiting for the belt to report movement.
+ Start sent — waiting for the belt to confirm it’s moving.
)
)}
@@ -194,7 +194,7 @@ export function Now({ onAmbient }: { onAmbient: () => void }) {
confirming === 'resume' ? 'pick back up' : 'start moving'
} at ${toMph(settings.value.targetKmh).toFixed(
1
- )} mph. Make sure it is clear and you are ready.`}
+ )} mph. Make sure it's clear and you're ready.`}
confirmLabel={confirming === 'resume' ? 'Resume' : 'Start'}
onConfirm={() => {
const kind = confirming;
diff --git a/apps/web/src/routes/Today.tsx b/apps/web/src/routes/Today.tsx
index 6b9e674..e87994b 100644
--- a/apps/web/src/routes/Today.tsx
+++ b/apps/web/src/routes/Today.tsx
@@ -100,13 +100,14 @@ export function Today() {
is the register every session is entered in, open one included. A third
statement of the same minutes is the habit this layout exists to break. */}
- {list.length === 0 && No walking recorded yet.
}
+ {list.length === 0 && No walks yet today.
}
{day.excluded > 0 && (
- {day.excluded} session{day.excluded === 1 ? '' : 's'} excluded from the
- distance total: recorded on a protocol whose distance scale this project had
- not established at the time, so summing it would invent a number.
+ {day.excluded} session{day.excluded === 1 ? ' is' : 's are'} left out of the
+ distance total. {day.excluded === 1 ? 'It was' : 'They were'} recorded before
+ the app could read this treadmill’s distance reliably, so adding{' '}
+ {day.excluded === 1 ? 'it' : 'them'} in would mean guessing.
)}
@@ -131,7 +132,7 @@ export function Today() {
height={isDesktop.value ? 150 : 120}
/>
) : (
- Nothing to trace yet. This fills in as the belt moves.
+ Nothing here yet. The chart fills in as the belt moves.
)}
@@ -157,9 +158,11 @@ export function Today() {
{pendingDelete && (
{
diff --git a/apps/web/src/state/connection.ts b/apps/web/src/state/connection.ts
index 7d39fea..b37577c 100644
--- a/apps/web/src/state/connection.ts
+++ b/apps/web/src/state/connection.ts
@@ -129,8 +129,8 @@ export async function connect({ filtered, name }: { filtered: boolean; name?: st
if (!navigator.bluetooth) {
fail(
new Error(
- 'This browser has no Web Bluetooth. Use Chrome, Edge, Opera or Samsung Internet — ' +
- 'Firefox and Safari (including on iOS) do not implement it.'
+ "This browser doesn't support Web Bluetooth. Use Chrome, Edge, Opera or Samsung " +
+ "Internet — Firefox and Safari (including on iOS) don't have it."
)
);
return;
@@ -318,7 +318,7 @@ function onDisconnected() {
// with stale UI state, is not a safe default.
trackEvent('disconnected', { by: 'device' });
log('device disconnected', 'err');
- setStatus('disconnected — belt keeps its current state, use its own controls', 'err');
+ setStatus("disconnected — the belt keeps doing what it was doing, so use the treadmill's own controls", 'err');
phase.value = 'error';
void teardown();
}
@@ -573,9 +573,9 @@ function watchForStart(kind: 'start' | 'resume' = 'start', refused = false) {
? `${refusedAll} — and the pad reports its child lock is on. Unlock it from the ` +
'panel, then try again.'
: asleep
- ? `${refusedAll} — the pad looks asleep: it never handed control to the app ` +
- 'and sent none of its settings when it connected. Wake it from its panel ' +
- `or remote, then press ${capitalise(kind)} again. Reconnecting will not help.`
+ ? `${refusedAll} — the pad looks asleep: it didn't hand control to the app or ` +
+ 'send its settings when it connected. Wake it from its panel or remote, ' +
+ `then press ${capitalise(kind)} again. Reconnecting will not help.`
: `${refusedAll} — its own panel still has control. Press a button on the ` +
"treadmill's panel or remote to wake it, then try again."
: `${capitalise(kind)} was sent but the belt never reported movement — it may ` +
@@ -695,8 +695,8 @@ async function bringUpToTarget(d: Driver, gen: number) {
// Checked before reporting, not after, which is the difference between a write
// that failed and a write nobody wanted any more. A pad that drops the link
// mid-write fails this one on the way out, and the disconnect has already put the
- // one message worth reading on the chip — "belt keeps its current state, use its
- // own controls". Overwriting it with a raw GATT error takes that away and counts
+ // one message worth reading on the chip — "the belt keeps doing what it was doing,
+ // so use the treadmill's own controls". Overwriting it with a raw GATT error takes that away and counts
// a `control_failed` against a command that was already moot.
if (gen !== speedGeneration) return;
// Otherwise: a failed speed write says nothing about whether the belt started, so
@@ -736,8 +736,8 @@ async function bringUpToTarget(d: Driver, gen: number) {
// target". The stepper is the way out, and it is one press away.
const stuck = mphText(live.value.speedKmh);
setStatus(
- `The belt is running at ${stuck} and will not take the ` +
- `${mphText(settings.value.targetKmh)} that was asked for. Press + or − to set it, ` +
+ `The belt is running at ${stuck} and won't go to the ` +
+ `${mphText(settings.value.targetKmh)} you set. Press + or − to try again, ` +
"or use the treadmill's own controls.",
'err'
);
diff --git a/docs/images/history.png b/docs/images/history.png
index fdb2bb3..1032c97 100644
Binary files a/docs/images/history.png and b/docs/images/history.png differ
diff --git a/docs/images/today.png b/docs/images/today.png
index b36289c..dddfcf7 100644
Binary files a/docs/images/today.png and b/docs/images/today.png differ