copy: remove second-person pronouns, use imperative tone
Replace "you/your" with impersonal alternatives across all user-facing strings in templates, CLI output, error messages, and documentation. Add copy style rule to CLAUDE.md.
This commit is contained in:
parent
3f672d7c06
commit
ba319e3ab7
18 changed files with 54 additions and 46 deletions
|
|
@ -693,6 +693,14 @@ Server-side via `q` query parameter. `ListQuery` extracts it, repos apply `LIKE`
|
|||
- Prefer `if`/`else` and `switch` over ternaries; never nest ternaries
|
||||
- **Inline `onclick` + global arrow functions** for pages with imperative JS — do not use `DOMContentLoaded` + `addEventListener`
|
||||
|
||||
### Copy & UI Text
|
||||
|
||||
- **No second-person pronouns** — never use "you", "your", "you're" in user-facing strings (templates, CLI output, error messages, docs). Use imperative tone or impersonal phrasing instead.
|
||||
- "Browse all your coffee data" → "Browse all coffee data"
|
||||
- "Lets you sign in" → "Enables sign-in"
|
||||
- "Your browser does not support" → "This browser does not support"
|
||||
- "you won't see it again" → "it will not be shown again"
|
||||
|
||||
### Naming & Conventions
|
||||
|
||||
- **Method naming**: `order_clause()` for sort query builders (not `sort_clause`)
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -46,8 +46,8 @@ No users found. Register the first user at:
|
|||
This link expires in 1 hour.
|
||||
```
|
||||
|
||||
Open that URL, choose a display name, and register a passkey. This creates your account and
|
||||
signs you in.
|
||||
Open that URL, choose a display name, and register a passkey. This creates an account and
|
||||
signs in automatically.
|
||||
|
||||
### CLI Authentication
|
||||
|
||||
|
|
@ -55,12 +55,12 @@ To use the CLI or API for write operations, create a token via browser handoff:
|
|||
|
||||
```bash
|
||||
brewlog token create --name "my-cli-token"
|
||||
# Browser opens → authenticate with your passkey → token printed once
|
||||
# Browser opens → authenticate with a passkey → token printed once
|
||||
|
||||
export BREWLOG_URL="http://localhost:3000"
|
||||
export BREWLOG_TOKEN="<token from above>"
|
||||
|
||||
# Now you can create data from the CLI
|
||||
# Create data from the CLI
|
||||
brewlog roaster add --name "Radical Roasters" --country "United Kingdom"
|
||||
```
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ directory is loaded automatically via [dotenvy](https://crates.io/crates/dotenvy
|
|||
|
||||
| Variable | Purpose | Default |
|
||||
| ------------------------ | ------------------------------------------------------ | --------------------- |
|
||||
| `BREWLOG_RP_ID` | WebAuthn Relying Party ID (your domain) | **required** |
|
||||
| `BREWLOG_RP_ID` | WebAuthn Relying Party ID (server domain) | **required** |
|
||||
| `BREWLOG_RP_ORIGIN` | WebAuthn Relying Party origin (full URL) | **required** |
|
||||
| `BREWLOG_DATABASE_URL` | Database connection string | `sqlite://brewlog.db` |
|
||||
| `BREWLOG_BIND_ADDRESS` | Server bind address | `127.0.0.1:3000` |
|
||||
|
|
|
|||
|
|
@ -87,14 +87,14 @@ async fn run_server(command: ServeCommand) -> Result<()> {
|
|||
let openrouter_api_key = command.openrouter_api_key.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"BREWLOG_OPENROUTER_API_KEY is required. Set this environment variable \
|
||||
to your OpenRouter API key for AI-powered extraction features."
|
||||
to an OpenRouter API key for AI-powered extraction features."
|
||||
)
|
||||
})?;
|
||||
|
||||
let foursquare_api_key = command.foursquare_api_key.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"BREWLOG_FOURSQUARE_API_KEY is required. Set this environment variable \
|
||||
to your Foursquare API key for nearby cafe search."
|
||||
to a Foursquare API key for nearby cafe search."
|
||||
)
|
||||
})?;
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ pub async fn create_token(client: &BrewlogClient, cmd: CreateTokenCommand) -> Re
|
|||
println!("Token Name: {}", cmd.name);
|
||||
println!("\nSave this token securely - it will not be shown again:");
|
||||
println!("\n{token}");
|
||||
println!("\nExport it in your environment:");
|
||||
println!("\nExport it as an environment variable:");
|
||||
println!(" export BREWLOG_TOKEN={token}");
|
||||
|
||||
Ok(())
|
||||
|
|
@ -159,7 +159,7 @@ async fn run_callback_server(
|
|||
let _ = sender.send(token);
|
||||
}
|
||||
|
||||
Html("<html><body><h1>Authenticated</h1><p>You can close this window and return to the terminal.</p></body></html>".to_string())
|
||||
Html("<html><body><h1>Authenticated</h1><p>This window can be closed. Return to the terminal.</p></body></html>".to_string())
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -266,8 +266,8 @@
|
|||
</div>
|
||||
{% else if grinder_options.is_empty() || brewer_options.is_empty() %}
|
||||
<div class="text-sm text-text-secondary">
|
||||
<h3 class="text-lg font-semibold text-text">Add your gear first</h3>
|
||||
<p class="mt-2">Brews need a grinder and brewer. Add your gear to enable this form.</p>
|
||||
<h3 class="text-lg font-semibold text-text">Add gear first</h3>
|
||||
<p class="mt-2">Brews need a grinder and brewer. Add gear to enable this form.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div>
|
||||
|
|
@ -449,7 +449,7 @@
|
|||
<div data-show="$_addType === 'gear'" style="display:none" class="rounded-lg border bg-surface p-5">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-text">New Gear</h3>
|
||||
<p class="mt-1 text-sm text-text-secondary">Add brewing equipment to your collection.</p>
|
||||
<p class="mt-1 text-sm text-text-secondary">Add brewing equipment.</p>
|
||||
</div>
|
||||
<form method="post" action="/api/v1/gear" class="mt-4 flex flex-col gap-4">
|
||||
<div class="grid gap-4 sm:grid-cols-3">
|
||||
|
|
@ -550,7 +550,7 @@
|
|||
{% else %}
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-text">New Cup</h3>
|
||||
<p class="mt-1 text-sm text-text-secondary">Record a coffee you had at a cafe.</p>
|
||||
<p class="mt-1 text-sm text-text-secondary">Record a coffee from a cafe visit.</p>
|
||||
</div>
|
||||
<form method="post" action="/api/v1/cups" class="mt-4 flex flex-col gap-4">
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
{% block content %}
|
||||
<header class="flex flex-col gap-2">
|
||||
<h1 class="text-3xl font-semibold">Admin</h1>
|
||||
<p class="max-w-2xl text-sm text-text-secondary">Manage your passkeys, API tokens, and data.</p>
|
||||
<p class="max-w-2xl text-sm text-text-secondary">Manage passkeys, API tokens, and data.</p>
|
||||
</header>
|
||||
|
||||
<!-- Passkeys -->
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-text">Passkeys</h2>
|
||||
<p class="mt-1 text-sm text-text-secondary">Passkeys let you sign in securely without a password.</p>
|
||||
<p class="mt-1 text-sm text-text-secondary">Passkeys enable secure sign-in without a password.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
</div>
|
||||
|
||||
{% if passkeys.len() <= 1 %}
|
||||
<p class="text-xs text-text-muted">Add another passkey before removing your only one.</p>
|
||||
<p class="text-xs text-text-muted">Add another passkey before removing the only one.</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- Add passkey form -->
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
</div>
|
||||
<div id="add-passkey-loading" class="mt-3 hidden flex items-center gap-3 text-sm text-accent">
|
||||
{{ icons::spinner("h-5 w-5") }}
|
||||
Follow the prompts from your browser or device...
|
||||
Follow the prompts from the browser or device...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -195,7 +195,7 @@
|
|||
>
|
||||
{{ icons::x_mark("h-4 w-4") }}
|
||||
</button>
|
||||
<p class="pr-6 text-sm font-medium text-green-800 dark:text-green-300">Token created! Copy it now — you won't see it again.</p>
|
||||
<p class="pr-6 text-sm font-medium text-green-800 dark:text-green-300">Token created! Copy it now — it will not be shown again.</p>
|
||||
<div class="mt-3 flex items-center gap-2">
|
||||
<code id="token-value" data-text="$_tokenValue"
|
||||
class="flex-1 rounded bg-surface px-3 py-2 text-sm font-mono text-text border border-green-200 dark:border-green-800 break-all select-all"></code>
|
||||
|
|
@ -402,7 +402,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
if (!confirm("Are you sure? All coffee data will be permanently deleted.")) {
|
||||
if (!confirm("Confirm? All coffee data will be permanently deleted.")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -434,7 +434,7 @@
|
|||
if (response.ok) {
|
||||
window.location.reload();
|
||||
} else if (response.status === 409) {
|
||||
alert("Cannot delete your only passkey. Add another passkey first.");
|
||||
alert("Cannot delete the only passkey. Add another passkey first.");
|
||||
} else {
|
||||
alert("Failed to delete passkey.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,11 +230,11 @@ block title %}Brewlog · Check In{% endblock %} {% block head %}
|
|||
<!-- Step 2: Roast identification -->
|
||||
<div class="mt-4" data-show="$_step === 2" style="display: none">
|
||||
<div class="rounded-lg border bg-surface p-5">
|
||||
<h3 class="text-base font-semibold text-text mb-3">What are you drinking?</h3>
|
||||
<h3 class="text-base font-semibold text-text mb-3">Select a coffee</h3>
|
||||
|
||||
<div data-show="!$_scanSuccess" class="mb-4">
|
||||
<p class="text-sm text-text-secondary mb-3" data-show="!$_extracting">
|
||||
Scan a bag to identify the coffee, or select from your existing roasts below.
|
||||
Scan a bag to identify the coffee, or select from existing roasts below.
|
||||
</p>
|
||||
|
||||
<form
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
{% block content %}
|
||||
<header class="flex flex-col gap-2">
|
||||
<h1 class="text-3xl font-semibold">Data</h1>
|
||||
<p class="max-w-2xl text-sm text-text-secondary">Browse all your coffee data.</p>
|
||||
<p class="max-w-2xl text-sm text-text-secondary">Browse all coffee data.</p>
|
||||
</header>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
<div class="rounded-lg border bg-surface p-6">
|
||||
<h1 class="text-2xl font-semibold text-accent">Login</h1>
|
||||
<p class="mt-2 text-sm text-text-secondary">
|
||||
Sign in with your passkey to manage your coffee log.
|
||||
Sign in with a passkey to access the coffee log.
|
||||
</p>
|
||||
|
||||
<div id="login-error" class="mt-4 hidden rounded-md bg-red-100 border border-red-300 p-3 text-sm text-red-800"></div>
|
||||
|
||||
<div id="login-unsupported" class="mt-4 hidden rounded-md bg-yellow-100 border border-yellow-300 p-3 text-sm text-yellow-800">
|
||||
Your browser does not support passkeys. Please use a modern browser (Chrome, Firefox, Safari, or Edge).
|
||||
This browser does not support passkeys. Please use a modern browser (Chrome, Firefox, Safari, or Edge).
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
<div class="rounded-lg border bg-surface p-6">
|
||||
<h1 class="text-2xl font-semibold text-accent">Register</h1>
|
||||
<p class="mt-2 text-sm text-text-secondary">
|
||||
Create your account by registering a passkey. This will be used to sign in going forward.
|
||||
Create an account by registering a passkey. This will be used to sign in going forward.
|
||||
</p>
|
||||
|
||||
<div id="register-error" class="mt-4 hidden rounded-md bg-red-100 border border-red-300 p-3 text-sm text-red-800"></div>
|
||||
|
||||
<div id="register-unsupported" class="mt-4 hidden rounded-md bg-yellow-100 border border-yellow-300 p-3 text-sm text-yellow-800">
|
||||
Your browser does not support passkeys. Please use a modern browser (Chrome, Firefox, Safari, or Edge).
|
||||
This browser does not support passkeys. Please use a modern browser (Chrome, Firefox, Safari, or Edge).
|
||||
</div>
|
||||
|
||||
<div id="register-form" class="mt-6 flex flex-col gap-4">
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
required
|
||||
autofocus
|
||||
class="input-field"
|
||||
placeholder="Your name"
|
||||
placeholder="Display name"
|
||||
/>
|
||||
</label>
|
||||
|
||||
|
|
@ -49,14 +49,14 @@
|
|||
</button>
|
||||
|
||||
<div id="register-loading" class="mt-2 hidden text-center text-sm text-text-muted">
|
||||
<p>Follow the prompts from your browser or device...</p>
|
||||
<p>Follow the prompts from the browser or device...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="register-success" class="mt-6 hidden">
|
||||
<div class="rounded-md bg-green-100 border border-green-300 p-4 text-sm text-green-800">
|
||||
<p class="font-semibold">Registration successful!</p>
|
||||
<p class="mt-1">Your passkey has been registered and you are now signed in.</p>
|
||||
<p class="mt-1">Passkey registered. Now signed in.</p>
|
||||
</div>
|
||||
<a
|
||||
href="/"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
<p class="text-center">
|
||||
{% if is_authenticated %}
|
||||
No bags added yet. Click the Add button to create your first bag.
|
||||
No bags added yet. Click Add to create the first bag.
|
||||
{% else %}
|
||||
No bags added yet.
|
||||
{% endif %}
|
||||
|
|
@ -135,7 +135,7 @@
|
|||
</table>
|
||||
</div>
|
||||
{% if bags.items.is_empty() %}
|
||||
<div class="p-8 text-center text-text-muted">No bags match your search.</div>
|
||||
<div class="p-8 text-center text-text-muted">No bags match the search.</div>
|
||||
{% endif %}
|
||||
{{ table::pagination_header(bags, navigator, "#bag-list") }}
|
||||
{% if bags.has_next() %}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
>
|
||||
<p class="text-center">
|
||||
{% if is_authenticated %}
|
||||
No brews logged yet. Click the Add button to log your first brew.
|
||||
No brews logged yet. Click Add to log the first brew.
|
||||
{% else %}
|
||||
No brews logged yet.
|
||||
{% endif %}
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
|
||||
{% if brews.items.is_empty() %}
|
||||
<div class="p-8 text-center text-text-muted">
|
||||
No brews match your search.
|
||||
No brews match the search.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
<p class="text-center">
|
||||
{% if is_authenticated %}
|
||||
No cafes added yet. Click the Add button to add your first cafe.
|
||||
No cafes added yet. Click Add to add the first cafe.
|
||||
{% else %}
|
||||
No cafes added yet.
|
||||
{% endif %}
|
||||
|
|
@ -110,7 +110,7 @@
|
|||
</table>
|
||||
</div>
|
||||
{% if cafes.items.is_empty() %}
|
||||
<div class="p-8 text-center text-text-muted">No cafes match your search.</div>
|
||||
<div class="p-8 text-center text-text-muted">No cafes match the search.</div>
|
||||
{% endif %}
|
||||
{{ table::pagination_header(cafes, navigator, "#cafe-list") }}
|
||||
{% if cafes.has_next() %}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
<p class="text-center">
|
||||
{% if is_authenticated %}
|
||||
No cups added yet. Click the Add button to add your first cup.
|
||||
No cups added yet. Click Add to add the first cup.
|
||||
{% else %}
|
||||
No cups added yet.
|
||||
{% endif %}
|
||||
|
|
@ -93,7 +93,7 @@
|
|||
</table>
|
||||
</div>
|
||||
{% if cups.items.is_empty() %}
|
||||
<div class="p-8 text-center text-text-muted">No cups match your search.</div>
|
||||
<div class="p-8 text-center text-text-muted">No cups match the search.</div>
|
||||
{% endif %}
|
||||
{{ table::pagination_header(cups, navigator, "#cup-list") }}
|
||||
{% if cups.has_next() %}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
<p class="text-center">
|
||||
{% if is_authenticated %}
|
||||
No gear added yet. Click the Add button to add your first piece of equipment.
|
||||
No gear added yet. Click Add to add the first piece of equipment.
|
||||
{% else %}
|
||||
No gear added yet.
|
||||
{% endif %}
|
||||
|
|
@ -90,7 +90,7 @@
|
|||
</table>
|
||||
</div>
|
||||
{% if gear.items.is_empty() %}
|
||||
<div class="p-8 text-center text-text-muted">No gear matches your search.</div>
|
||||
<div class="p-8 text-center text-text-muted">No gear matches the search.</div>
|
||||
{% endif %}
|
||||
{{ table::pagination_header(gear, navigator, "#gear-list") }}
|
||||
{% if gear.has_next() %}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
<p class="text-center">
|
||||
{% if is_authenticated %}
|
||||
No roasts added yet. Click the Add button to create your first roast.
|
||||
No roasts added yet. Click Add to create the first roast.
|
||||
{% else %}
|
||||
No roasts added yet.
|
||||
{% endif %}
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
</table>
|
||||
</div>
|
||||
{% if roasts.items.is_empty() %}
|
||||
<div class="p-8 text-center text-text-muted">No roasts match your search.</div>
|
||||
<div class="p-8 text-center text-text-muted">No roasts match the search.</div>
|
||||
{% endif %}
|
||||
{{ table::pagination_header(roasts, navigator, "#roast-list") }}
|
||||
{% if roasts.has_next() %}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
<p class="text-center">
|
||||
{% if is_authenticated %}
|
||||
No roasters added yet. Click the Add button to create your first roaster.
|
||||
No roasters added yet. Click Add to create the first roaster.
|
||||
{% else %}
|
||||
No roasters added yet.
|
||||
{% endif %}
|
||||
|
|
@ -110,7 +110,7 @@
|
|||
</table>
|
||||
</div>
|
||||
{% if roasters.items.is_empty() %}
|
||||
<div class="p-8 text-center text-text-muted">No roasters match your search.</div>
|
||||
<div class="p-8 text-center text-text-muted">No roasters match the search.</div>
|
||||
{% endif %}
|
||||
{{ table::pagination_header(roasters, navigator, "#roaster-list") }}
|
||||
{% if roasters.has_next() %}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const locateUser = (triggerEl) => {
|
|||
root.dispatchEvent(new CustomEvent(name, { detail, bubbles: true }));
|
||||
|
||||
if (!navigator.geolocation) {
|
||||
emit('location-error', { message: 'Geolocation not supported by your browser.' });
|
||||
emit('location-error', { message: 'Geolocation not supported by this browser.' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ const locateUser = (triggerEl) => {
|
|||
</button>
|
||||
<input type="text" data-bind:_city-name
|
||||
class="input-field w-full"
|
||||
data-attr:placeholder="$_locationFound ? 'Using your location (' + $_userLat.toFixed(2) + ', ' + $_userLng.toFixed(2) + ')' : 'Name of city or area'"
|
||||
data-attr:placeholder="$_locationFound ? 'Using current location (' + $_userLat.toFixed(2) + ', ' + $_userLng.toFixed(2) + ')' : 'Name of city or area'"
|
||||
data-attr:disabled="$_locationFound"
|
||||
data-on:input__debounce.350ms="$_cityName.length >= 2 && $_cafeSearch.length >= 2 && @get('/api/v1/nearby-cafes?near=' + encodeURIComponent($_cityName) + '&q=' + encodeURIComponent($_cafeSearch), {responseOverrides: {selector: '#nearby-results', mode: 'replace'}})"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue