brand: standardize "Upbeat Bytes" → "upbeatBytes" everywhere
Per the logo + brand: the name is upbeatBytes (camelCase). Swept all user-facing strings — titles/og:site_name/og:title, logo alt text, share pages (share.py), emails (email_send), classifier prompt (llm), digest/unsubscribe (api), PWA manifest, game share text, sign-in, the SPA shell + patch-static-heads (play title) — plus README/publish.sh and the email test fixture. (SMTP From env was already upbeatBytes.) Domains (upbeatbytes.com) unchanged. 425 BE + 36 FE green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+3
-3
@@ -767,7 +767,7 @@ def create_app() -> FastAPI:
|
||||
ok = _do_unsubscribe(u, t)
|
||||
msg = (
|
||||
"You’re unsubscribed from the daily digest. No hard feelings — "
|
||||
"Upbeat Bytes is always here when you want it."
|
||||
"upbeatBytes is always here when you want it."
|
||||
if ok else
|
||||
"That unsubscribe link looks invalid or expired. You can manage the "
|
||||
"digest from your account settings."
|
||||
@@ -776,9 +776,9 @@ def create_app() -> FastAPI:
|
||||
'<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">'
|
||||
'<div style="max-width:520px;margin:12vh auto;padding:0 24px;text-align:center;'
|
||||
'font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#16263a">'
|
||||
'<h1 style="font-size:22px">Upbeat Bytes</h1>'
|
||||
'<h1 style="font-size:22px">upbeatBytes</h1>'
|
||||
f'<p style="font-size:16px;line-height:1.5;color:#3b4754">{msg}</p>'
|
||||
'<p><a href="/" style="color:#0083ad;text-decoration:none">← Back to Upbeat Bytes</a></p></div>'
|
||||
'<p><a href="/" style="color:#0083ad;text-decoration:none">← Back to upbeatBytes</a></p></div>'
|
||||
)
|
||||
return HTMLResponse(html)
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ def send_email(to: str, subject: str, text: str, html: str | None = None, reply_
|
||||
|
||||
def send_feedback(to: str, category: str, message: str, contact: str | None, who: str) -> None:
|
||||
"""Notify the admin of new user feedback (plain text is plenty here)."""
|
||||
subject = f"Upbeat Bytes feedback · {category}"
|
||||
subject = f"upbeatBytes feedback · {category}"
|
||||
reply = contact or "(none given)"
|
||||
text = (
|
||||
f"New feedback ({category})\n"
|
||||
@@ -74,14 +74,14 @@ def send_feedback_reply(to: str, reply_text: str, reply_html: str | None, origin
|
||||
"""Reply to a reader's feedback from the admin inbox. Sends multipart
|
||||
text/plain + text/html (the HTML is the pre-sanitized Markdown render). Quotes
|
||||
their original note for context; exposes no analytics/account details."""
|
||||
subject = "Re: Your Upbeat Bytes feedback"
|
||||
subject = "Re: Your upbeatBytes feedback"
|
||||
quoted = "\n".join("> " + line for line in (original_message or "").splitlines())
|
||||
text = (
|
||||
f"{reply_text}\n\n"
|
||||
"—\n"
|
||||
"In reply to your note to Upbeat Bytes:\n"
|
||||
"In reply to your note to upbeatBytes:\n"
|
||||
f"{quoted}\n\n"
|
||||
"Thanks for reaching out.\n— Upbeat Bytes\n"
|
||||
"Thanks for reaching out.\n— upbeatBytes\n"
|
||||
)
|
||||
body_html = None
|
||||
if reply_html:
|
||||
@@ -90,9 +90,9 @@ def send_feedback_reply(to: str, reply_text: str, reply_html: str | None, origin
|
||||
'<div style="font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;'
|
||||
'color:#16263a;font-size:15px;line-height:1.5">'
|
||||
f"{reply_html}"
|
||||
'<p style="color:#5d6b78;font-size:13px;margin-top:20px">In reply to your note to Upbeat Bytes:</p>'
|
||||
'<p style="color:#5d6b78;font-size:13px;margin-top:20px">In reply to your note to upbeatBytes:</p>'
|
||||
f'<blockquote style="color:#5d6b78;border-left:3px solid #e8e3d8;margin:0;padding-left:12px">{oq}</blockquote>'
|
||||
"<p>Thanks for reaching out.<br>— Upbeat Bytes</p></div>"
|
||||
"<p>Thanks for reaching out.<br>— upbeatBytes</p></div>"
|
||||
)
|
||||
# Route the reader's reply to our chosen inbox (never back to the reader).
|
||||
cfg = _cfg()
|
||||
@@ -102,9 +102,9 @@ def send_feedback_reply(to: str, reply_text: str, reply_html: str | None, origin
|
||||
|
||||
def send_magic_link(to: str, link: str) -> None:
|
||||
"""Send a calm, single-purpose sign-in email."""
|
||||
subject = "Your Upbeat Bytes sign-in link"
|
||||
subject = "Your upbeatBytes sign-in link"
|
||||
text = (
|
||||
"Welcome back to Upbeat Bytes.\n\n"
|
||||
"Welcome back to upbeatBytes.\n\n"
|
||||
f"Tap to sign in:\n{link}\n\n"
|
||||
"This link works once and expires in 15 minutes.\n"
|
||||
"If you didn't request it, you can safely ignore this email."
|
||||
@@ -113,7 +113,7 @@ def send_magic_link(to: str, link: str) -> None:
|
||||
html = (
|
||||
'<div style="font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;'
|
||||
'color:#16263a;line-height:1.6">'
|
||||
"<p>Welcome back to <strong>Upbeat Bytes</strong>.</p>"
|
||||
"<p>Welcome back to <strong>upbeatBytes</strong>.</p>"
|
||||
f'<p><a href="{safe}" style="display:inline-block;background:#0083ad;color:#fff;'
|
||||
'text-decoration:none;padding:11px 20px;border-radius:999px;font-weight:600">'
|
||||
"Sign in</a></p>"
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ _RESPONSE_FORMATS = (
|
||||
)
|
||||
|
||||
|
||||
SYSTEM_PROMPT = """You classify article metadata for Upbeat Bytes, a calm news digest.
|
||||
SYSTEM_PROMPT = """You classify article metadata for upbeatBytes, a calm news digest.
|
||||
|
||||
The bar is NOT "is this happy?" — it is "will a reader finish this calm or a little better, never worse?" ACCEPT stories that are calm, neutral, insightful, or uplifting: they inform, teach, delight, or show progress or benefit. Neutral-but-absorbing is welcome — a discovery, a clear explainer, a clever build or gadget, a fascinating bit of science, space, nature, design, or culture, a genuinely useful insight — even when it isn't "feel-good."
|
||||
|
||||
|
||||
+13
-13
@@ -20,7 +20,7 @@ def _tag(name: str, content: str | None, attr: str = "property") -> str:
|
||||
def render_share_page(article: dict, base_url: str, summary: str | None = None,
|
||||
explanation: dict | None = None) -> str:
|
||||
aid = article["id"]
|
||||
title = (article.get("title") or "Upbeat Bytes").strip()
|
||||
title = (article.get("title") or "upbeatBytes").strip()
|
||||
why = (article.get("reason_text") or article.get("description")
|
||||
or "A calm, constructive story worth your attention.").strip()
|
||||
source = (article.get("source_name") or "the source").strip()
|
||||
@@ -39,7 +39,7 @@ def render_share_page(article: dict, base_url: str, summary: str | None = None,
|
||||
twitter_card = "summary_large_image" if image else "summary"
|
||||
|
||||
meta = "\n".join(filter(None, [
|
||||
_tag("og:site_name", "Upbeat Bytes"),
|
||||
_tag("og:site_name", "upbeatBytes"),
|
||||
_tag("og:type", "article"),
|
||||
_tag("og:title", title),
|
||||
_tag("og:description", why),
|
||||
@@ -138,7 +138,7 @@ def render_share_page(article: dict, base_url: str, summary: str | None = None,
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{escape(title)} · Upbeat Bytes</title>
|
||||
<title>{escape(title)} · upbeatBytes</title>
|
||||
<meta name="description" content="{escape(why)}">
|
||||
<link rel="canonical" href="{escape(page_url)}">
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
@@ -194,7 +194,7 @@ def render_share_page(article: dict, base_url: str, summary: str | None = None,
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bar"><div class="inner"><a href="/"><img src="/logo.svg" alt="Upbeat Bytes"></a><button class="back" type="button" data-back aria-label="Go back"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M11 6l-6 6 6 6" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round"/></svg>Back</button></div></div>
|
||||
<div class="bar"><div class="inner"><a href="/"><img src="/logo.svg" alt="upbeatBytes"></a><button class="back" type="button" data-back aria-label="Go back"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M11 6l-6 6 6 6" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round"/></svg>Back</button></div></div>
|
||||
<main class="wrap">
|
||||
<article class="card">
|
||||
{media}
|
||||
@@ -207,9 +207,9 @@ def render_share_page(article: dict, base_url: str, summary: str | None = None,
|
||||
<div class="actions">
|
||||
<a class="primary" href="{escape(src_url)}" target="_blank" rel="noopener" data-src-click>Read the full story at {escape(source)}</a>
|
||||
<button class="secondary" type="button" data-share>Copy link</button>
|
||||
<a class="secondary" href="/news">Explore Upbeat Bytes →</a>
|
||||
<a class="secondary" href="/news">Explore upbeatBytes →</a>
|
||||
</div>
|
||||
<p class="note">Upbeat Bytes summarizes in its own words and links to the original publisher — it doesn't host the article.</p>
|
||||
<p class="note">upbeatBytes summarizes in its own words and links to the original publisher — it doesn't host the article.</p>
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
@@ -275,7 +275,7 @@ def render_digest(items: list[dict], base_url: str, brief_date: str | None) -> s
|
||||
)
|
||||
|
||||
meta = "\n".join(filter(None, [
|
||||
_tag("og:site_name", "Upbeat Bytes"),
|
||||
_tag("og:site_name", "upbeatBytes"),
|
||||
_tag("og:type", "website"),
|
||||
_tag("og:title", "Today's good news, summarized"),
|
||||
_tag("og:description", intro),
|
||||
@@ -292,7 +292,7 @@ def render_digest(items: list[dict], base_url: str, brief_date: str | None) -> s
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Today's good news, summarized · Upbeat Bytes</title>
|
||||
<title>Today's good news, summarized · upbeatBytes</title>
|
||||
<meta name="description" content="{escape(intro)}">
|
||||
<link rel="canonical" href="{page_url}">
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
@@ -324,12 +324,12 @@ def render_digest(items: list[dict], base_url: str, brief_date: str | None) -> s
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bar"><div class="inner"><a href="/"><img src="/logo.svg" alt="Upbeat Bytes"></a></div></div>
|
||||
<div class="bar"><div class="inner"><a href="/"><img src="/logo.svg" alt="upbeatBytes"></a></div></div>
|
||||
<main class="wrap">
|
||||
<h1>Today's good news</h1>
|
||||
<p class="lede">{escape(intro)}{f' · {escape(brief_date)}' if brief_date else ''}</p>
|
||||
{cards}
|
||||
<p class="more"><a href="/news">Browse more on Upbeat Bytes →</a></p>
|
||||
<p class="more"><a href="/news">Browse more on upbeatBytes →</a></p>
|
||||
</main>
|
||||
</body>
|
||||
</html>"""
|
||||
@@ -339,7 +339,7 @@ def render_not_found(base_url: str) -> str:
|
||||
return f"""<!doctype html>
|
||||
<html lang="en"><head><meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Story not found · Upbeat Bytes</title><link rel="icon" href="/favicon.svg">
|
||||
<title>Story not found · upbeatBytes</title><link rel="icon" href="/favicon.svg">
|
||||
<style>
|
||||
body {{ margin:0; min-height:100vh; display:flex; flex-direction:column; align-items:center;
|
||||
justify-content:center; gap:14px; text-align:center; padding:40px;
|
||||
@@ -348,8 +348,8 @@ def render_not_found(base_url: str) -> str:
|
||||
a {{ color:#006b8e; }}
|
||||
</style></head>
|
||||
<body>
|
||||
<img src="/logo.svg" alt="Upbeat Bytes" style="height:44px">
|
||||
<img src="/logo.svg" alt="upbeatBytes" style="height:44px">
|
||||
<h1 style="font-family:Georgia,serif;font-weight:600">That story isn't here</h1>
|
||||
<p style="color:#5d6b78">It may have moved on — the good news refreshes often.</p>
|
||||
<a href="/">← Back to Upbeat Bytes</a>
|
||||
<a href="/">← Back to upbeatBytes</a>
|
||||
</body></html>"""
|
||||
|
||||
Reference in New Issue
Block a user