From aaf532732fdf60d24eb1f331c589b4056e8c0463 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 27 Oct 2026 09:18:02 +0000 Subject: [PATCH 1/2] Finish the visible DebateLab to DebateOwl rename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header, footer, titles, manifest, emails, and link preview now use the product name from site.json. The one-time notice still says DebateLab is becoming DebateOwl. Routes, storage keys, and the domain stay the same. Co-authored-by: Gáborjáni-Szabó Ádám --- .claude/skills/verify-debatelab/SKILL.md | 6 +- .../features/auth-demo-login.md | 2 +- .../verify-debatelab/features/localization.md | 4 +- .../verify-debatelab/recipes/auth.spec.ts | 4 +- .../recipes/current-debate.spec.ts | 2 +- .../recipes/localization.spec.ts | 16 ++-- agentic/pages.e2e.ts | 2 +- ...35a31518cb6002dad033bbe5549c162f9df6e.json | 6 +- ...c45338886c52fc41ea6a7c0f6fd1435dfbfec.json | 4 +- ...af500077ddc18df231c58d9c29724467af6ac.json | 6 +- ...281513bd8832a317f7d3d65ac987fec417dc4.json | 4 +- e2e/first-run.spec.ts | 4 +- e2e/free-tools.spec.ts | 4 +- e2e/full-stack/fake-openrouter.mjs | 4 +- e2e/full-stack/first-run.spec.ts | 4 +- e2e/home-navigation.spec.ts | 2 +- e2e/hungarian-parity.spec.ts | 4 +- e2e/loading-performance.spec.ts | 6 +- e2e/localization.spec.ts | 14 ++-- e2e/production/canary.spec.ts | 2 +- e2e/startup-shell.spec.ts | 4 +- e2e/voice-rounds.spec.ts | 6 +- embed/debate-timer/index.html | 4 +- index.html | 30 ++++---- public/ai-debate-case-generator/index.html | 24 +++--- public/ai-debate-coach/index.html | 30 ++++---- public/ai-debate-practice/index.html | 32 ++++---- public/ai-rebuttal-generator/index.html | 26 +++---- public/argument-flowchart/index.html | 30 ++++---- public/best-ai-debate-tools/index.html | 36 ++++----- .../british-parliamentary-debate/index.html | 26 +++---- public/congress-debate/index.html | 26 +++---- public/debate-clash-points/index.html | 26 +++---- public/debate-evidence-finder/index.html | 32 ++++---- public/debate-fallacies/index.html | 26 +++---- public/debate-judge-adaptation/index.html | 26 +++---- public/debate-motion-analysis/index.html | 28 +++---- public/debate-rebuttal-practice/index.html | 30 ++++---- public/debate-speech-structure/index.html | 26 +++---- public/debate-topics-for-students/index.html | 26 +++---- public/debatelab-pricing/index.html | 42 +++++------ public/debatelab-vs-chatgpt/index.html | 40 +++++----- public/debatelab-vs-debateclub/index.html | 40 +++++----- public/debatelab-vs-windebate/index.html | 40 +++++----- public/hero-visual.jpg | Bin 105734 -> 102903 bytes .../how-to-cross-examine-in-debate/index.html | 28 +++---- public/how-to-flow-in-debate/index.html | 26 +++---- public/how-to-practice-rebuttals/index.html | 28 +++---- .../index.html | 26 +++---- public/how-to-weigh-in-debate/index.html | 26 +++---- public/how-to-write-a-debate-case/index.html | 30 ++++---- public/humans.txt | 2 +- public/lincoln-douglas-debate/index.html | 26 +++---- public/llms.txt | 58 +++++++-------- public/manifest.webmanifest | 4 +- public/online-debate-timer/index.html | 28 +++---- public/policy-debate/index.html | 26 +++---- public/public-forum-debate/index.html | 30 ++++---- public/sw.js | 2 +- public/theme-init.js | 6 +- public/world-schools-debate/index.html | 30 ++++---- scripts/generate_seo_content.py | 44 +++++------ src/App.tsx | 3 +- .../features/export/GoogleDriveAuthModal.tsx | 2 +- .../workspace/WorkspacePracticePanel.tsx | 2 +- src/components/landing/Footer.tsx | 3 +- src/components/landing/Navbar.tsx | 3 +- src/components/layout/AppLayout.tsx | 5 +- src/components/layout/PublicPageShell.tsx | 3 +- src/config/site.json | 4 +- src/lib/debateowl-intro.ts | 4 +- src/lib/free-tools/formats.ts | 2 +- src/lib/pdf-export-utils.ts | 5 +- src/lib/seo/pages.ts | 4 +- src/lib/seo/use-page-head.ts | 6 +- src/locales/en.json | 70 +++++++++--------- src/locales/es.json | 70 +++++++++--------- src/locales/hu.json | 70 +++++++++--------- src/locales/tools/en.json | 4 +- src/locales/tools/es.json | 4 +- src/locales/tools/hu.json | 4 +- src/pages/LoginPage.tsx | 2 +- src/pages/tools/DebateTimerToolPage.tsx | 4 +- src/pages/tools/FlowSheetToolPage.tsx | 2 +- src/pages/tools/MotionGeneratorToolPage.tsx | 4 +- .../tools/SpeechTimeCalculatorToolPage.tsx | 2 +- src/pages/tools/ToolsHubPage.tsx | 2 +- src/test/onboarding/debateowl-intro.test.tsx | 2 +- src/test/pages/SharedPage.test.tsx | 10 +-- src/test/pages/SparringVoice.test.tsx | 2 +- src/test/seo/page-head.test.tsx | 12 +-- src/test/vercel-deployment-config.test.ts | 4 +- supabase/config.toml | 4 +- .../functions/_shared/auto-model-router.ts | 8 +- supabase/functions/_shared/entitlements.ts | 6 +- supabase/functions/_shared/prompts.test.ts | 4 +- supabase/functions/_shared/prompts/common.ts | 4 +- .../functions/_shared/prompts/crossfire.ts | 2 +- .../_shared/prompts/prompt-examples.test.ts | 2 +- .../functions/_shared/prompts/sparring.ts | 4 +- supabase/functions/ai-stream/prompt.test.ts | 2 +- supabase/functions/workspace-invites/index.ts | 4 +- supabase/templates/confirmation.html | 2 +- supabase/templates/magic_link.html | 2 +- 104 files changed, 770 insertions(+), 764 deletions(-) diff --git a/.claude/skills/verify-debatelab/SKILL.md b/.claude/skills/verify-debatelab/SKILL.md index 4858f5d08..3bf777bd3 100644 --- a/.claude/skills/verify-debatelab/SKILL.md +++ b/.claude/skills/verify-debatelab/SKILL.md @@ -1,11 +1,11 @@ --- name: verify-debatelab -description: Launch and drive the real DebateLab web app (React client + local Supabase Auth/Postgres/Edge Functions, with either a fake model provider or real OpenRouter pinned to Claude Haiku 5.5 on the Anthropic BYOK key plus real Exa research) to prove a user-facing feature works, with screenshots, ARIA snapshots, provider calls, and database rows as evidence. Use when asked to verify, demo, reproduce, or screenshot DebateLab behavior in a browser, or before calling a DebateLab UI/Edge Function change done. +description: Launch and drive the real DebateOwl web app (React client + local Supabase Auth/Postgres/Edge Functions, with either a fake model provider or real OpenRouter pinned to Claude Haiku 5.5 on the Anthropic BYOK key plus real Exa research) to prove a user-facing feature works, with screenshots, ARIA snapshots, provider calls, and database rows as evidence. Use when asked to verify, demo, reproduce, or screenshot DebateOwl behavior in a browser, or before calling a DebateOwl UI/Edge Function change done. --- -# Verify DebateLab +# Verify DebateOwl -DebateLab is a web app. The surface a user touches is the browser client at `/login` and `/app/*`, backed by Supabase Auth, Postgres/RLS, and Deno Edge Functions. This skill runs all of that locally and drives it with Playwright. It has two modes: +DebateOwl is a web app. The surface a user touches is the browser client at `/login` and `/app/*`, backed by Supabase Auth, Postgres/RLS, and Deno Edge Functions. This skill runs all of that locally and drives it with Playwright. It has two modes: - **Fake (default, free).** `e2e/full-stack/fake-openrouter.mjs` answers every model call with canned text. Proves wiring, rows, and UI, not model behavior. - **Live (`up --live`, costs money).** The same container becomes a recording proxy to real OpenRouter. It rewrites every chat call to `anthropic/claude-haiku-5.5` pinned to the Anthropic provider with fallbacks off, so it bills the Anthropic BYOK key and not OpenRouter credits (override with `VERIFY_LIVE_MODEL`, which must be an `anthropic/` model), records OpenRouter's reply model as proof, and stops forwarding after `--max-calls` (default 400). Edge Functions also get a real Exa key, so web research and verified citations are real. diff --git a/.claude/skills/verify-debatelab/features/auth-demo-login.md b/.claude/skills/verify-debatelab/features/auth-demo-login.md index 5fc35d217..a86ec6afd 100644 --- a/.claude/skills/verify-debatelab/features/auth-demo-login.md +++ b/.claude/skills/verify-debatelab/features/auth-demo-login.md @@ -38,7 +38,7 @@ Preconditions: ## Gotchas -- A first sign-in on a new account starts the onboarding tour on `/app`. Its first step (`Welcome to DebateLab!`, `Start your first debate here.`) offers `Start debate`. The fixtures pre-seed `debate-trainer-onboarding` to skip it. The tour is tracked per account (store version 1, `completedUserIds`). A version 0 seed with `hasCompletedOnboarding: true` covers only the next account that signs in, so a second account in the same browser sees the tour. Interactive runs click `Skip tour` (`[data-test-id="button-skip"]`). The tour itself is covered in [current-debate](./current-debate.md). +- A first sign-in on a new account starts the onboarding tour on `/app`. Its first step (`Welcome to DebateOwl!`, `Start your first debate here.`) offers `Start debate`. The fixtures pre-seed `debate-trainer-onboarding` to skip it. The tour is tracked per account (store version 1, `completedUserIds`). A version 0 seed with `hasCompletedOnboarding: true` covers only the next account that signs in, so a second account in the same browser sees the tour. Interactive runs click `Skip tour` (`[data-test-id="button-skip"]`). The tour itself is covered in [current-debate](./current-debate.md). - Sessions are stored under the `debate-auth-local` localStorage key in local-preview mode. Don't reuse stub-session helpers (`e2e/helpers/auth.ts`). They fake auth and prove rendering only. - Google sign-in is not wired to the local stack. Demo codes, email codes and the email link work here. - The revoked-session check forces one token refresh before it signs out. An Auth outage (network error, 5xx, 429) keeps the session, and that path is covered by `src/test/auth/session-expiry.test.ts`, not by a recipe. diff --git a/.claude/skills/verify-debatelab/features/localization.md b/.claude/skills/verify-debatelab/features/localization.md index 74ae95853..2e7942b6b 100644 --- a/.claude/skills/verify-debatelab/features/localization.md +++ b/.claude/skills/verify-debatelab/features/localization.md @@ -1,10 +1,10 @@ # Localization -The interface runs in English, Spanish and Hungarian. Every page names itself in the tab title in the interface language ("Generálás · DebateLab" in the app, "Felhasználási feltételek | DebateLab by Pi-Stack" on public pages), form validation and server refusals read in that language, the language switcher sits in every header, and `?lang=es|hu|en` links and the Hungarian and Spanish pages (`/hu`, `/es`, `/hu/tools/…`, `/es/tools/…`) open the app in that language. +The interface runs in English, Spanish and Hungarian. Every page names itself in the tab title in the interface language ("Generálás · DebateOwl" in the app, "Felhasználási feltételek | DebateOwl by Pi-Stack" on public pages), form validation and server refusals read in that language, the language switcher sits in every header, and `?lang=es|hu|en` links and the Hungarian and Spanish pages (`/hu`, `/es`, `/hu/tools/…`, `/es/tools/…`) open the app in that language. ## Sub-features -- loc-titles: `document.title` on app and public routes, per language, and a switch retitling the page without a reload. App routes read ` · DebateLab` (`usePrivateRouteHead`); public pages use the `pageHead` catalog section (`usePageHead`), whose English text is also what the prerender writes. +- loc-titles: `document.title` on app and public routes, per language, and a switch retitling the page without a reload. App routes read ` · DebateOwl` (`usePrivateRouteHead`); public pages use the `pageHead` catalog section (`usePageHead`), whose English text is also what the prerender writes. - loc-validation: an empty Attack submit shows `validation.caseTextRequired` in the interface language, and nothing shows before the first submit. Generate, Brainstorm and Factoids use the same keys. - loc-switcher: the switcher on `/login`, the 404 page, and the other public pages (`PublicPageShell`), besides the landing page and the app header. - loc-links: `/?lang=hu` switches and saves the language, sets ``, and drops the parameter; `/es` and `/hu` are prerendered pages of their own that keep their address, and Start from `/hu` leads into the app in Hungarian. diff --git a/.claude/skills/verify-debatelab/recipes/auth.spec.ts b/.claude/skills/verify-debatelab/recipes/auth.spec.ts index 709775760..bf5dba314 100644 --- a/.claude/skills/verify-debatelab/recipes/auth.spec.ts +++ b/.claude/skills/verify-debatelab/recipes/auth.spec.ts @@ -176,7 +176,7 @@ test("auth-deep-link-email-code: a signed-out deep link signs in by email code a await page.goto("/app/generate"); await expect(page).toHaveURL(/\/login$/); await expect(page.getByRole("main")).toHaveCount(1); - await expect(page.getByRole("heading", { level: 1, name: "Sign in to DebateLab" })).toBeVisible(); + await expect(page.getByRole("heading", { level: 1, name: "Sign in to DebateOwl" })).toBeVisible(); await evidence.aria("01-login-from-deep-link"); let sentAfter = Date.now(); @@ -230,7 +230,7 @@ test("auth-switch-account: the next account on this browser starts on /app, gets await evidence.step("Signed in as a second account; landed on /app, not /app/settings"); // AUTH-07: the second account gets its own tour. - const tour = page.getByText("Welcome to DebateLab!"); + const tour = page.getByText("Welcome to DebateOwl!"); await expect(tour).toBeVisible({ timeout: 10_000 }); await evidence.shot("01-second-account-tour"); if (await page.getByRole("button", { name: "Open menu" }).isVisible()) { diff --git a/.claude/skills/verify-debatelab/recipes/current-debate.spec.ts b/.claude/skills/verify-debatelab/recipes/current-debate.spec.ts index 4d800217b..c823436b3 100644 --- a/.claude/skills/verify-debatelab/recipes/current-debate.spec.ts +++ b/.claude/skills/verify-debatelab/recipes/current-debate.spec.ts @@ -198,7 +198,7 @@ test("debate-tour: a brand-new account starts its first debate from the onboardi const tooltip = page.locator(".react-joyride__tooltip"); const next = page.locator('[data-test-id="button-primary"]'); - await expect(tooltip).toContainText("Welcome to DebateLab!", { timeout: 15_000 }); + await expect(tooltip).toContainText("Welcome to DebateOwl!", { timeout: 15_000 }); await expect(tooltip).toContainText("Start your first debate here."); await evidence.shot("01-tour-first-step"); diff --git a/.claude/skills/verify-debatelab/recipes/localization.spec.ts b/.claude/skills/verify-debatelab/recipes/localization.spec.ts index 9f8d18b01..7373b9d45 100644 --- a/.claude/skills/verify-debatelab/recipes/localization.spec.ts +++ b/.claude/skills/verify-debatelab/recipes/localization.spec.ts @@ -30,10 +30,10 @@ async function chooseLanguage(page: Page, language: keyof typeof LANGUAGE_NAMES) await expect(page.locator("html")).toHaveAttribute("lang", language); } -/** The tab title once the page has set its own (the shell's is just "DebateLab"). */ +/** The tab title once the page has set its own (the shell's is just "DebateOwl"). */ async function pageTitle(page: Page, route: string) { await page.goto(route); - await expect.poll(() => page.title()).not.toBe("DebateLab"); + await expect.poll(() => page.title()).not.toBe("DebateOwl"); return page.title(); } @@ -49,13 +49,13 @@ test("loc-titles: every route's tab title is its own and follows the interface l await evidence.json("01-document-titles", titles); for (const route of APP_ROUTES) { - for (const language of ["en", "hu", "es"]) expect(titles[language][route]).toMatch(/ · DebateLab$/); + for (const language of ["en", "hu", "es"]) expect(titles[language][route]).toMatch(/ · DebateOwl$/); } for (const route of PUBLIC_ROUTES) { for (const language of ["en", "hu", "es"]) expect(titles[language][route]).toMatch(new RegExp(`\\| ${BRAND}$`)); } - expect(titles.en["/app/generate"]).toBe(`${en.nav.generate} · DebateLab`); - expect(titles.hu["/app/generate"]).toBe(`${hu.nav.generate} · DebateLab`); + expect(titles.en["/app/generate"]).toBe(`${en.nav.generate} · DebateOwl`); + expect(titles.hu["/app/generate"]).toBe(`${hu.nav.generate} · DebateOwl`); expect(titles.hu["/no-such-page"]).toBe(hu.pageHead.notFound.title.replace("{{brand}}", BRAND)); // Each language names every page in its own words (the brand aside). for (const route of [...APP_ROUTES, ...PUBLIC_ROUTES]) { @@ -65,9 +65,9 @@ test("loc-titles: every route's tab title is its own and follows the interface l // A switch retitles the page on screen, with no reload. await page.goto("/app/attack"); await chooseLanguage(page, "en"); - await expect(page).toHaveTitle(`${en.nav.attack} · DebateLab`); + await expect(page).toHaveTitle(`${en.nav.attack} · DebateOwl`); await chooseLanguage(page, "hu"); - await expect(page).toHaveTitle(`${hu.nav.attack} · DebateLab`); + await expect(page).toHaveTitle(`${hu.nav.attack} · DebateOwl`); await evidence.shot("02-attack-title-hu"); }); @@ -96,7 +96,7 @@ test("loc-switcher: sign-in and not-found pages carry the language switcher", as await evidence.shot("01-login-en"); await chooseLanguage(page, "hu"); await expect(page.getByRole("heading", { level: 1, name: hu.login.title })).toBeVisible(); - // "Bejelentkezés a DebateLabba" already names the app, so it gets no suffix. + // "Bejelentkezés a DebateOwlba" already names the app, so it gets no suffix. await expect(page).toHaveTitle(hu.login.title); await evidence.shot("02-login-hu"); diff --git a/agentic/pages.e2e.ts b/agentic/pages.e2e.ts index 9be9bf0b3..813a86b68 100644 --- a/agentic/pages.e2e.ts +++ b/agentic/pages.e2e.ts @@ -30,7 +30,7 @@ const PUBLIC_PAGES = ["/", "/login", "/terms", "/privacy", "/expocit/sources"]; const NAMES: Record = { en: "English", es: "Spanish", hu: "Hungarian" }; const EXEMPT = - "Product names such as DebateLab and ExpoCit, AI model names (Claude Sonnet 5), plan names (Pro), debate format codes and the competition format names kept in English in every language (Public Forum, Lincoln-Douglas, Policy, British Parliamentary, World Schools, Asian Parliamentary), language names offered as choices (English, Español, Magyar), and user content do not count. Technical loanwords the app uses on purpose, such as email, chat, prompt and token, count as translated."; + "Product names such as DebateOwl, DebateLab and ExpoCit, AI model names (Claude Sonnet 5), plan names (Pro), debate format codes and the competition format names kept in English in every language (Public Forum, Lincoln-Douglas, Policy, British Parliamentary, World Schools, Asian Parliamentary), language names offered as choices (English, Español, Magyar), and user content do not count. Technical loanwords the app uses on purpose, such as email, chat, prompt and token, count as translated."; async function checkPage(browser: Browser, agent: Agent, language: Language) { await expect(browser.locator("html")).toHaveAttribute("lang", language); diff --git a/agentic/replay/a7ab22475ff220f06530d1b208935a31518cb6002dad033bbe5549c162f9df6e.json b/agentic/replay/a7ab22475ff220f06530d1b208935a31518cb6002dad033bbe5549c162f9df6e.json index 21e936b24..23cec4a2d 100644 --- a/agentic/replay/a7ab22475ff220f06530d1b208935a31518cb6002dad033bbe5549c162f9df6e.json +++ b/agentic/replay/a7ab22475ff220f06530d1b208935a31518cb6002dad033bbe5549c162f9df6e.json @@ -98,7 +98,7 @@ "goneAnchors": [ { "role": "alertdialog", - "name": "Welcome to DebateLab! 🎉Everything in DebateLab belongs to one debate: the motion you are preparing, its sources, and your results. Start your first debate here.Start debate" + "name": "Welcome to DebateOwl! 🎉Everything in DebateOwl belongs to one debate: the motion you are preparing, its sources, and your results. Start your first debate here.Start debate" }, { "role": "button", @@ -113,10 +113,10 @@ }, { "role": "heading", - "name": "Welcome to DebateLab! 🎉" + "name": "Welcome to DebateOwl! 🎉" }, { - "text": "Everything in DebateLab belongs to one debate: the motion you are preparing, its sources, and your results. Start your first debate here." + "text": "Everything in DebateOwl belongs to one debate: the motion you are preparing, its sources, and your results. Start your first debate here." }, { "role": "button", diff --git a/agentic/replay/cc6903d8aafb702ef5a496a05b9c45338886c52fc41ea6a7c0f6fd1435dfbfec.json b/agentic/replay/cc6903d8aafb702ef5a496a05b9c45338886c52fc41ea6a7c0f6fd1435dfbfec.json index 8f3f7580b..a11e55982 100644 --- a/agentic/replay/cc6903d8aafb702ef5a496a05b9c45338886c52fc41ea6a7c0f6fd1435dfbfec.json +++ b/agentic/replay/cc6903d8aafb702ef5a496a05b9c45338886c52fc41ea6a7c0f6fd1435dfbfec.json @@ -38,7 +38,7 @@ "endAnchors": [ { "role": "image", - "name": "Logotipo de DebateLab" + "name": "Logotipo de DebateOwl" }, { "text": "Herramientas" @@ -69,7 +69,7 @@ "goneAnchors": [ { "role": "image", - "name": "DebateLab logo" + "name": "DebateOwl logo" }, { "text": "Tools" diff --git a/agentic/replay/cec2ee4bacb9750f2fa98e3124caf500077ddc18df231c58d9c29724467af6ac.json b/agentic/replay/cec2ee4bacb9750f2fa98e3124caf500077ddc18df231c58d9c29724467af6ac.json index 99d066293..24e0d292d 100644 --- a/agentic/replay/cec2ee4bacb9750f2fa98e3124caf500077ddc18df231c58d9c29724467af6ac.json +++ b/agentic/replay/cec2ee4bacb9750f2fa98e3124caf500077ddc18df231c58d9c29724467af6ac.json @@ -98,7 +98,7 @@ "goneAnchors": [ { "role": "alertdialog", - "name": "Welcome to DebateLab! 🎉Everything in DebateLab belongs to one debate: the motion you are preparing, its sources, and your results. Start your first debate here.Start debate" + "name": "Welcome to DebateOwl! 🎉Everything in DebateOwl belongs to one debate: the motion you are preparing, its sources, and your results. Start your first debate here.Start debate" }, { "role": "button", @@ -113,10 +113,10 @@ }, { "role": "heading", - "name": "Welcome to DebateLab! 🎉" + "name": "Welcome to DebateOwl! 🎉" }, { - "text": "Everything in DebateLab belongs to one debate: the motion you are preparing, its sources, and your results. Start your first debate here." + "text": "Everything in DebateOwl belongs to one debate: the motion you are preparing, its sources, and your results. Start your first debate here." }, { "role": "button", diff --git a/agentic/replay/d15dccac4ac18fdabdb7d4c086d281513bd8832a317f7d3d65ac987fec417dc4.json b/agentic/replay/d15dccac4ac18fdabdb7d4c086d281513bd8832a317f7d3d65ac987fec417dc4.json index 36e643e31..883031931 100644 --- a/agentic/replay/d15dccac4ac18fdabdb7d4c086d281513bd8832a317f7d3d65ac987fec417dc4.json +++ b/agentic/replay/d15dccac4ac18fdabdb7d4c086d281513bd8832a317f7d3d65ac987fec417dc4.json @@ -38,7 +38,7 @@ "endAnchors": [ { "role": "image", - "name": "Logotipo de DebateLab" + "name": "Logotipo de DebateOwl" }, { "role": "button", @@ -70,7 +70,7 @@ "goneAnchors": [ { "role": "image", - "name": "DebateLab logo" + "name": "DebateOwl logo" }, { "role": "button", diff --git a/e2e/first-run.spec.ts b/e2e/first-run.spec.ts index 27e765436..42c4811b3 100644 --- a/e2e/first-run.spec.ts +++ b/e2e/first-run.spec.ts @@ -8,7 +8,7 @@ const USER_ID = '00000000-0000-0000-0000-000000000001'; const copy = { en: { - welcome: 'Welcome to DebateLab!', + welcome: 'Welcome to DebateOwl!', firstDebate: 'Start your first debate here.', startDebate: 'Start debate', motionField: 'Motion', @@ -24,7 +24,7 @@ const copy = { gateMotion: 'This House would abolish zoos', }, hu: { - welcome: 'Üdvözlünk a DebateLabban!', + welcome: 'Üdvözlünk a DebateOwlban!', firstDebate: 'Itt indíthatod el az első vitádat.', startDebate: 'Vita indítása', motionField: 'Tétel', diff --git a/e2e/free-tools.spec.ts b/e2e/free-tools.spec.ts index 193148139..c3b729577 100644 --- a/e2e/free-tools.spec.ts +++ b/e2e/free-tools.spec.ts @@ -46,7 +46,7 @@ test.describe('free debate tools', { tag: '@smoke' }, () => { expect(response?.ok()).toBeTruthy(); await expect(page.getByRole('heading', { level: 1, name: tool.h1 })).toBeVisible(); await expect(page.locator('link[rel="canonical"]')).toHaveAttribute('href', `${ORIGIN}${tool.path}`); - await expect(page).toHaveTitle(/DebateLab/); + await expect(page).toHaveTitle(/DebateOwl/); const graphs = await page.locator('script[type="application/ld+json"]').allTextContents(); const types = graphs.flatMap((json) => (JSON.parse(json)['@graph'] as { '@type': string }[]).map((node) => node['@type'])); expect(types).toContain(tool.type); @@ -122,7 +122,7 @@ test.describe('free debate tools', { tag: '@smoke' }, () => { await expect(page.getByRole('heading', { level: 2, name: 'First Proposition' })).toBeVisible(); await expect(page.locator('meta[name="robots"]')).toHaveAttribute('content', 'noindex'); await expect(page.locator('link[rel="canonical"]')).toHaveAttribute('href', `${ORIGIN}/tools/debate-timer`); - const attribution = page.getByRole('link', { name: 'Timer by DebateLab' }); + const attribution = page.getByRole('link', { name: 'Timer by DebateOwl' }); await expect(attribution).toHaveAttribute('href', `${ORIGIN}/tools/debate-timer`); await expect(attribution).not.toHaveAttribute('rel', /nofollow/); await expect(page.getByLabel('Format')).toHaveCount(0); diff --git a/e2e/full-stack/fake-openrouter.mjs b/e2e/full-stack/fake-openrouter.mjs index e7d64c0d0..4f48206d2 100644 --- a/e2e/full-stack/fake-openrouter.mjs +++ b/e2e/full-stack/fake-openrouter.mjs @@ -14,7 +14,7 @@ const port = Number(process.env.PORT ?? 8787); const upstream = process.env.UPSTREAM; const liveModel = process.env.LIVE_MODEL; const chatModel = (requested) => (liveModel === "as-requested" ? requested : liveModel); -// Claude runs only on DebateLab's Anthropic key (BYOK in OpenRouter): every +// Claude runs only on DebateOwl's Anthropic key (BYOK in OpenRouter): every // Claude call leaves pinned to the Anthropic provider with fallbacks off, and // any `models` fallback list is dropped. A rewrite to another family (only // as-requested reaches one) keeps just the options that still apply to it. @@ -191,7 +191,7 @@ const routes = [ ["brainstorm", "Brainstorm arguments like experienced competitive debaters", brainstorm], ["factoids", "You are a debate research specialist", factoids], ["sparring", "live debate sparring opponent", (user, system) => sparring(user, system)], - ["crossfire", "You are DebateLab's crossfire coach", (user) => crossfire(user)], + ["crossfire", "You are DebateOwl's crossfire coach", (user) => crossfire(user)], ["ask", "You are a debate coach chat bot", ask], ]; diff --git a/e2e/full-stack/first-run.spec.ts b/e2e/full-stack/first-run.spec.ts index eabae851a..cbd21045f 100644 --- a/e2e/full-stack/first-run.spec.ts +++ b/e2e/full-stack/first-run.spec.ts @@ -10,7 +10,7 @@ const copy = { demoTab: "Demo code", demoCode: "Demo code", signIn: "Sign in with demo code", - welcome: "Welcome to DebateLab!", + welcome: "Welcome to DebateOwl!", firstDebate: "Start your first debate here.", startDebate: "Start debate", motionField: "Motion", @@ -29,7 +29,7 @@ const copy = { demoTab: "Demókód", demoCode: "Demókód", signIn: "Bejelentkezés demókóddal", - welcome: "Üdvözlünk a DebateLabban!", + welcome: "Üdvözlünk a DebateOwlban!", firstDebate: "Itt indíthatod el az első vitádat.", startDebate: "Vita indítása", motionField: "Tétel", diff --git a/e2e/home-navigation.spec.ts b/e2e/home-navigation.spec.ts index b6c6637d5..d6c8344f1 100644 --- a/e2e/home-navigation.spec.ts +++ b/e2e/home-navigation.spec.ts @@ -116,7 +116,7 @@ test('header keeps the brand and controls inside a 320px viewport', async ({ pag const debateControl = page.getByTestId('current-debate-mobile'); const controls = [languageControl, menuControl, debateControl]; - await expect(page.getByAltText('DebateLab logo')).toBeVisible(); + await expect(page.getByAltText('DebateOwl logo')).toBeVisible(); for (const control of controls) await expect(control).toBeVisible(); const boxes = await Promise.all(controls.map((control) => control.boundingBox())); diff --git a/e2e/hungarian-parity.spec.ts b/e2e/hungarian-parity.spec.ts index 2b3650064..a4de66ff1 100644 --- a/e2e/hungarian-parity.spec.ts +++ b/e2e/hungarian-parity.spec.ts @@ -49,7 +49,7 @@ test.describe('Hungarian parity', () => { await expectHungarianDocument(page); for (const [path, heading] of [ - ['/login', 'Bejelentkezés a DebateLabba'], + ['/login', 'Bejelentkezés a DebateOwlba'], ['/terms', 'Felhasználási feltételek'], ['/privacy', 'Adatvédelmi irányelvek'], ['/expocit/sources', 'Források'], @@ -79,7 +79,7 @@ test.describe('Hungarian parity', () => { await expectHungarianDocument(page); await expect(page.getByRole('heading', { name: 'Felkészülés' })).toBeVisible({ timeout: 15_000 }); - await expect(page.locator('.react-joyride__tooltip')).toContainText('Üdvözlünk a DebateLabban!', { timeout: 15_000 }); + await expect(page.locator('.react-joyride__tooltip')).toContainText('Üdvözlünk a DebateOwlban!', { timeout: 15_000 }); await expect(page.locator('[data-test-id="button-primary"]')).toContainText('Következő'); await expect(page.locator('[data-test-id="button-skip"]')).toContainText('Túra kihagyása'); await page.locator('[data-test-id="button-skip"]').click(); diff --git a/e2e/loading-performance.spec.ts b/e2e/loading-performance.spec.ts index 16b6613dc..a4cd48d73 100644 --- a/e2e/loading-performance.spec.ts +++ b/e2e/loading-performance.spec.ts @@ -19,7 +19,7 @@ test('a stalled Jam recorder cannot delay the real application', async ({ page, await page.evaluate(() => window.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }))); await jamRequest; await expect(page.getByRole('heading', { name: /: AI debate prep and practice$/ })).toBeVisible(); - await expect(page.getByText('DebateLab by Pi-Stack is the official')).toHaveCount(0); + await expect(page.getByText('DebateOwl by Pi-Stack is the official')).toHaveCount(0); }); test('pagehide removes the Jam frame and a persisted pageshow does not re-add it (synthetic events, stubbed Jam)', async ({ page, browserName }) => { @@ -56,7 +56,7 @@ test('public routes keep the real UI after refresh', async ({ page }) => { await page.goto(path); await page.reload(); await expect(page.getByRole(path === '/' ? 'heading' : 'button', { name: label })).toBeVisible(); - await expect(page.getByText('DebateLab by Pi-Stack is the official')).toHaveCount(0); + await expect(page.getByText('DebateOwl by Pi-Stack is the official')).toHaveCount(0); } }); @@ -72,7 +72,7 @@ test('authenticated routes keep the shell and current page after refresh', async await page.reload(); await expect(page.locator('header')).toBeVisible(); await expect(page.getByRole('heading', { name: heading, exact: true })).toBeVisible(); - await expect(page.getByText('DebateLab by Pi-Stack is the official')).toHaveCount(0); + await expect(page.getByText('DebateOwl by Pi-Stack is the official')).toHaveCount(0); } }); diff --git a/e2e/localization.spec.ts b/e2e/localization.spec.ts index ebf92f9c8..5cc9eede0 100644 --- a/e2e/localization.spec.ts +++ b/e2e/localization.spec.ts @@ -60,26 +60,26 @@ test('every route has its own tab title in each language, and a switch retitles titles[language] = {}; for (const route of [...APP_ROUTES, ...PUBLIC_ROUTES]) { await page.goto(route); - await expect.poll(() => page.title(), { timeout: 30_000 }).not.toBe('DebateLab'); + await expect.poll(() => page.title(), { timeout: 30_000 }).not.toBe('DebateOwl'); titles[language][route] = await page.title(); } } writeFileSync(test.info().outputPath('document-titles.json'), JSON.stringify(titles, null, 2)); for (const language of ['en', 'es', 'hu'] as const) { - for (const route of APP_ROUTES) expect(titles[language][route]).toMatch(/ · DebateLab$/); - for (const route of PUBLIC_ROUTES) expect(titles[language][route]).toMatch(/ \| DebateLab by Pi-Stack$/); + for (const route of APP_ROUTES) expect(titles[language][route]).toMatch(/ · DebateOwl$/); + for (const route of PUBLIC_ROUTES) expect(titles[language][route]).toMatch(/ \| DebateOwl by Pi-Stack$/); } - expect(titles.hu['/app/attack']).toBe(`${hu.nav.attack} · DebateLab`); + expect(titles.hu['/app/attack']).toBe(`${hu.nav.attack} · DebateOwl`); expect(titles.es['/no-such-page']).toBe(brand(es.pageHead.notFound.title)); for (const route of [...APP_ROUTES, ...PUBLIC_ROUTES]) expect(titles.hu[route], route).not.toBe(titles.en[route]); // No reload: the switcher retitles the page on screen. await page.goto('/app/generate'); await choose(page, 'en'); - await expect(page).toHaveTitle(`${en.nav.generate} · DebateLab`); + await expect(page).toHaveTitle(`${en.nav.generate} · DebateOwl`); await choose(page, 'hu'); - await expect(page).toHaveTitle(`${hu.nav.generate} · DebateLab`); + await expect(page).toHaveTitle(`${hu.nav.generate} · DebateOwl`); }); test('an empty Attack says the case is required in the interface language, only after submit', async ({ page }) => { @@ -105,7 +105,7 @@ test('sign-in and not-found pages carry the language switcher', async ({ page }) await shot(page, '01-login-en'); await choose(page, 'hu'); await expect(page.getByRole('heading', { level: 1, name: hu.login.title })).toBeVisible(); - // "Bejelentkezés a DebateLabba" already names the app, so it gets no suffix. + // "Bejelentkezés a DebateOwlba" already names the app, so it gets no suffix. await expect(page).toHaveTitle(hu.login.title); await shot(page, '02-login-hu'); diff --git a/e2e/production/canary.spec.ts b/e2e/production/canary.spec.ts index 5d7d739a3..53a6d18c5 100644 --- a/e2e/production/canary.spec.ts +++ b/e2e/production/canary.spec.ts @@ -44,7 +44,7 @@ test("the production site loads and guards the app", async ({ page, baseURL }) = const errors: string[] = []; page.on("pageerror", (error) => errors.push(error.message)); await page.goto("/"); - await expect(page).toHaveTitle(/DebateLab/); + await expect(page).toHaveTitle(/DebateOwl/); await page.goto("/login"); await expect(page.getByRole("button", { name: "Continue with Google" })).toBeVisible(); await page.goto("/app/generate"); diff --git a/e2e/startup-shell.spec.ts b/e2e/startup-shell.spec.ts index d7a010daa..5f6a4fd41 100644 --- a/e2e/startup-shell.spec.ts +++ b/e2e/startup-shell.spec.ts @@ -16,7 +16,7 @@ test('shows a dark private startup shell while the entry module is delayed', { t await page.goto('/app', { waitUntil: 'commit' }); await expect(page.locator('[data-startup-shell]')).toBeVisible(); - await expect(page.locator('[data-startup-status]')).toHaveText('Loading DebateLab'); + await expect(page.locator('[data-startup-status]')).toHaveText('Loading DebateOwl'); await expect.poll(() => page.evaluate(() => ({ appShell: document.documentElement.classList.contains('app-shell'), dark: document.documentElement.classList.contains('dark'), @@ -49,6 +49,6 @@ test('shows a recoverable message when the static main entry fails', { tag: '@sm await page.goto('/app', { waitUntil: 'domcontentloaded' }); await expect(page.locator('[data-bootstrap-error]')).toBeVisible(); - await expect(page.getByRole('heading', { name: 'Unable to load DebateLab' })).toBeVisible(); + await expect(page.getByRole('heading', { name: 'Unable to load DebateOwl' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Reload' })).toBeEnabled(); }); diff --git a/e2e/voice-rounds.spec.ts b/e2e/voice-rounds.spec.ts index 5d2280289..08d039c33 100644 --- a/e2e/voice-rounds.spec.ts +++ b/e2e/voice-rounds.spec.ts @@ -110,7 +110,7 @@ async function startVoiceSparring(page: Page) { await page.getByTestId('voice-mode').click(); const notice = page.getByTestId('voice-notice'); - await expect(notice).toContainText('DebateLab never records or stores your audio.'); + await expect(notice).toContainText('DebateOwl never records or stores your audio.'); await expect(notice).toContainText('In Chrome, that sends your audio to Google'); await expect(notice).toContainText('sent as text to ElevenLabs'); await page.getByRole('button', { name: 'Turn on voice' }).click(); @@ -255,8 +255,8 @@ test('voice mode is hidden with a reason where the browser cannot recognise spee }); for (const [locale, label, noticeLine, privacyLine] of [ - ['es', 'Ronda con voz', 'DebateLab nunca graba ni guarda tu audio.', 'ElevenLabs para la voz del oponente'], - ['hu', 'Hangos kör', 'A DebateLab soha nem rögzíti és nem tárolja a hangodat.', 'az ElevenLabsszel az ellenfél hangjához'], + ['es', 'Ronda con voz', 'DebateOwl nunca graba ni guarda tu audio.', 'ElevenLabs para la voz del oponente'], + ['hu', 'Hangos kör', 'A DebateOwl soha nem rögzíti és nem tárolja a hangodat.', 'az ElevenLabsszel az ellenfél hangjához'], ] as const) { test(`the voice notice and the privacy page are translated (${locale})`, async ({ page }) => { await setLocale(page, locale); diff --git a/embed/debate-timer/index.html b/embed/debate-timer/index.html index e6c78a8c4..644920858 100644 --- a/embed/debate-timer/index.html +++ b/embed/debate-timer/index.html @@ -6,7 +6,7 @@ - Debate timer | DebateLab + Debate timer | DebateOwl @@ -16,7 +16,7 @@
diff --git a/index.html b/index.html index 5d6862a9c..cdadefd00 100644 --- a/index.html +++ b/index.html @@ -8,9 +8,9 @@ - DebateLab - - + DebateOwl + + @@ -21,18 +21,18 @@ - - - + + + - + - - + + - +