From 225dc84b205595bb420784d8e17b65ece6eba04a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 6 Oct 2026 10:38:19 +0000 Subject: [PATCH] Fix first-run onboarding UX: naming, locale, voices, Heute route, mobile drawer Co-authored-by: cynarAI --- apps/web/e2e/first-run-onboarding-ux.spec.ts | 76 ++++++++++++++ apps/web/e2e/helpers.ts | 15 ++- apps/web/e2e/heute-brief.spec.ts | 5 +- apps/web/e2e/onboarding-conversation.spec.ts | 11 +-- apps/web/src/App.tsx | 4 + .../onboarding/OnboardingNameStep.tsx | 98 +++++++++++++++++++ .../onboarding/OnboardingVoiceStep.tsx | 26 +++-- apps/web/src/components/stage/OoStage.tsx | 15 ++- apps/web/src/lib/app-routes.ts | 5 + apps/web/src/lib/group-chat.test.ts | 14 +++ apps/web/src/lib/group-chat.ts | 4 + apps/web/src/lib/onboarding-voices.test.ts | 25 +++++ apps/web/src/lib/onboarding-voices.ts | 23 +++++ apps/web/src/locales/de/messages.po | 31 +++++- apps/web/src/locales/en/messages.po | 31 +++++- apps/web/src/locales/es/messages.po | 29 +++++- apps/web/src/locales/hi/messages.po | 29 +++++- apps/web/src/locales/ko/messages.po | 29 +++++- apps/web/src/locales/pt-BR/messages.po | 29 +++++- apps/web/src/locales/ru/messages.po | 29 +++++- apps/web/src/locales/tr/messages.po | 29 +++++- apps/web/src/locales/zh-CN/messages.po | 29 +++++- apps/web/src/pages/Onboarding.tsx | 62 +++++++++--- apps/web/src/pages/Shell.tsx | 27 ++++- .../pages/shell/sidebar-toggle-buttons.tsx | 1 + .../web/src/pages/shell/use-mobile-sidebar.ts | 15 +++ 26 files changed, 641 insertions(+), 50 deletions(-) create mode 100644 apps/web/e2e/first-run-onboarding-ux.spec.ts create mode 100644 apps/web/src/components/onboarding/OnboardingNameStep.tsx create mode 100644 apps/web/src/lib/app-routes.ts create mode 100644 apps/web/src/lib/group-chat.test.ts create mode 100644 apps/web/src/lib/group-chat.ts create mode 100644 apps/web/src/lib/onboarding-voices.test.ts create mode 100644 apps/web/src/lib/onboarding-voices.ts diff --git a/apps/web/e2e/first-run-onboarding-ux.spec.ts b/apps/web/e2e/first-run-onboarding-ux.spec.ts new file mode 100644 index 0000000..6b74d84 --- /dev/null +++ b/apps/web/e2e/first-run-onboarding-ux.spec.ts @@ -0,0 +1,76 @@ +import { expect, test } from "@playwright/test"; +import { completeOnboarding, rpc, signup } from "./helpers"; + +test("onboarding names the first Oo and shows it in chat", async ({ page }) => { + const stamp = Date.now(); + await signup(page, `name-oo-${stamp}@rakazo.test`, "password12", "Namer"); + await page.waitForURL(/\/onboarding/, { timeout: 20_000 }); + await page.getByRole("button", { name: "Continue", exact: true }).click(); + await expect(page.getByTestId("onboarding-name-step")).toBeVisible(); + await page.getByTestId("onboarding-oo-name-input").fill("Luna"); + await page.getByTestId("onboarding-name-step").getByRole("button", { name: "Continue" }).click(); + await page.getByRole("button", { name: /Skip/i }).click(); + await page.getByRole("button", { name: "Skip", exact: true }).click(); + await expect(page).toHaveURL(/\/app\/g\/[^/]+/, { timeout: 30_000 }); + await expect(page.getByTestId("bot-settings-trigger").getByText("Luna", { exact: true })).toBeVisible(); + expect((await rpc<{ name: string }>(page, "bots/ensureMain", {})).name).toBe("Luna"); +}); + +test("German UI keeps Deutsch on language skip label", async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, "language", { value: "de-DE", configurable: true }); + }); + const stamp = Date.now(); + await signup(page, `de-skip-${stamp}@rakazo.test`, "password12", "Deutsch"); + await page.getByRole("button", { name: "Continue", exact: true }).click(); + await page.getByRole("button", { name: "Skip", exact: true }).click(); + await expect(page.getByRole("heading", { name: /Sprache wählen|Choose your language/i })).toBeVisible(); + await expect(page.getByRole("button", { name: /Überspringen · Deutsch/i })).toBeVisible(); +}); + +test("German voice step lists German voices after choosing Deutsch", async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, "language", { value: "de-DE", configurable: true }); + }); + const stamp = Date.now(); + await signup(page, `de-voice-${stamp}@rakazo.test`, "password12", "Stimme"); + await page.getByRole("button", { name: "Continue", exact: true }).click(); + await page.getByRole("button", { name: "Skip", exact: true }).click(); + await page.getByRole("button", { name: "Deutsch", exact: true }).click(); + await expect(page.getByRole("button", { name: "Eva", exact: true })).toBeVisible(); + await expect(page.getByRole("button", { name: "Alloy", exact: true })).toHaveCount(0); + await expect(page.getByRole("button", { name: "Eva", exact: true })).toHaveAttribute( + "aria-pressed", + "true", + ); +}); + +test("main conversation does not show a group subtitle for one Oo", async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, "language", { value: "de-DE", configurable: true }); + }); + const stamp = Date.now(); + await signup(page, `main-1-1-${stamp}@rakazo.test`, "password12", "Solo"); + await completeOnboarding(page, undefined, { mainConversation: true }); + await expect(page.getByTestId("bot-settings-trigger")).not.toContainText("Gruppenunterhaltung"); + await expect(page.getByTestId("oo-group-stage")).toContainText(/1:1/i); +}); + +test.describe("mobile shell", () => { + test.use({ + hasTouch: true, + isMobile: true, + viewport: { width: 390, height: 844 }, + }); + + test("Escape closes the mobile navigation drawer", async ({ page }) => { + const stamp = Date.now(); + await signup(page, `mobile-esc-${stamp}@rakazo.test`, "password12", "Escape"); + await completeOnboarding(page); + await page.getByRole("button", { name: "Open navigation" }).click(); + await expect(page.getByRole("button", { name: "Close navigation" })).toBeVisible(); + await page.keyboard.press("Escape"); + await expect(page.getByRole("button", { name: "Close navigation" })).toHaveCount(0); + await expect(page.getByTestId("open-navigation")).toBeFocused(); + }); +}); diff --git a/apps/web/e2e/helpers.ts b/apps/web/e2e/helpers.ts index 04661ec..be58823 100644 --- a/apps/web/e2e/helpers.ts +++ b/apps/web/e2e/helpers.ts @@ -95,23 +95,25 @@ async function visible(locator: Locator, timeout: number) { export async function completeOnboarding( page: Page, testInfo?: TestInfo, - options: { mainConversation?: boolean } = {}, + options: { mainConversation?: boolean; ooName?: string } = {}, ) { await page.waitForURL(/\/(onboarding|app)/, { timeout: 20_000 }); if (page.url().includes("/onboarding")) { await expect(page.locator('[data-rakazo-surface="onboarding"]')).toHaveAttribute( "data-onboarding-step", - /^(emoji|language|voice|integrations|scan|bot)$/, + /^(emoji|name|language|voice|integrations|scan|bot)$/, { timeout: 30_000 }, ); } const design = page.getByRole("heading", { name: /Design your Oo/i }); + const naming = page.getByTestId("onboarding-name-step"); const language = page.getByRole("heading", { name: /Choose your language/i }); const voice = page.getByRole("heading", { name: /Pick a voice/i }); const integrations = page.getByRole("heading", { name: "Server integrations", exact: true }); - const chief = page.getByText("Chief").first(); + const expectedOoName = options.ooName ?? "Chief"; + const chief = page.getByText(expectedOoName).first(); // Scripted CI has needsModel false, so a fresh owner starts on integrations. // A model-gated run still opens the look picker first. @@ -120,6 +122,13 @@ export async function completeOnboarding( if (await design.isVisible()) { await page.getByRole("button", { name: "Continue", exact: true }).click(); + await expect(naming).toBeVisible(); + if (options.ooName) { + await page.getByTestId("onboarding-oo-name-input").fill(options.ooName); + await naming.getByRole("button", { name: "Continue", exact: true }).click(); + } else { + await page.getByRole("button", { name: "Skip", exact: true }).click(); + } await expect(language).toBeVisible(); await page.getByRole("button", { name: /Skip/i }).click(); await expect(voice).toBeVisible(); diff --git a/apps/web/e2e/heute-brief.spec.ts b/apps/web/e2e/heute-brief.spec.ts index 293dcc9..612fc4e 100644 --- a/apps/web/e2e/heute-brief.spec.ts +++ b/apps/web/e2e/heute-brief.spec.ts @@ -18,7 +18,10 @@ test("Heute shows native brief instead of Mission Control iframe", async ({ page await completeOnboarding(page); await openHeute(page); - await expect(page).toHaveURL(/\/app\/[^/]+$/); + await expect(page).toHaveURL(/\/app\/heute$/); + await page.reload(); + await expect(page.getByTestId("heute-brief")).toBeVisible(); + await expect(page).toHaveURL(/\/app\/heute$/); const brief = page.getByTestId("heute-brief"); await expect(brief).toBeVisible(); diff --git a/apps/web/e2e/onboarding-conversation.spec.ts b/apps/web/e2e/onboarding-conversation.spec.ts index 7809caa..c1d254b 100644 --- a/apps/web/e2e/onboarding-conversation.spec.ts +++ b/apps/web/e2e/onboarding-conversation.spec.ts @@ -11,7 +11,7 @@ test("focus choice suggests apps and preserves a completed connection", async ({ const stamp = Date.now(); await signup(page, `onboarding-${stamp}@rakazo.test`, "password12", "Robin"); // The main bot greets the user in the main conversation, where onboarding lands. - await completeOnboarding(page, undefined, { mainConversation: true }); + await completeOnboarding(page, undefined, { mainConversation: true, ooName: "Robin" }); await expect( page.getByText("Hey Robin. Fresh start on my side, so I’ll keep this short."), @@ -31,12 +31,11 @@ test("focus choice suggests apps and preserves a completed connection", async ({ await captureScreenshot(page, testInfo, "choice-card-onboarding"); await page.getByTestId("focus-choice-day").click(); - // The focus step suggests apps but must not rename the bot: the name the - // user chose during creation ("Chief") is preserved. + // The focus step must not rename the bot: onboarding keeps the chosen name. await expect( - page.getByTestId("bot-settings-trigger").getByText("Chief", { exact: true }), + page.getByTestId("bot-settings-trigger").getByText("Robin", { exact: true }), ).toBeVisible(); - expect((await rpc<{ name: string }>(page, "bots/ensureMain", {})).name).toBe("Chief"); + expect((await rpc<{ name: string }>(page, "bots/ensureMain", {})).name).toBe("Robin"); await expect(page.getByRole("combobox", { name: "Message" })).toBeVisible(); // The app cards speak for themselves; onboarding adds no text around them. await expect(page.getByText(/^Got it/)).toHaveCount(0); @@ -103,7 +102,7 @@ test("focus choice suggests apps and preserves a completed connection", async ({ test("choice refresh failures leave options available for retry", async ({ page }) => { await signup(page, `choice-refresh-${Date.now()}@rakazo.test`, "password12", "Choice Retry"); - await completeOnboarding(page, undefined, { mainConversation: true }); + await completeOnboarding(page, undefined, { mainConversation: true, ooName: "Robin" }); const choice = page.getByTestId("focus-choice-day"); await expect(choice).toBeEnabled(); // Keep the existing choice rendered while its save succeeds and navigation refresh fails. diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 7d13621..3b1e012 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -120,6 +120,10 @@ function SessionApp() { } /> : } /> + : } + /> : } diff --git a/apps/web/src/components/onboarding/OnboardingNameStep.tsx b/apps/web/src/components/onboarding/OnboardingNameStep.tsx new file mode 100644 index 0000000..c7d9c1e --- /dev/null +++ b/apps/web/src/components/onboarding/OnboardingNameStep.tsx @@ -0,0 +1,98 @@ +import { Trans, useLingui } from "@lingui/react/macro"; +import type { EvooPose } from "@rakazo/ui-web"; +import { BotAvatar, Button } from "@rakazo/ui-web"; +import { useId, useState } from "react"; +import type { OnboardingEmojiConfig } from "./OnboardingOoDesigner"; + +export const DEFAULT_FIRST_OO_NAME = "Chief"; + +type OnboardingNameStepProps = { + config: OnboardingEmojiConfig; + name: string; + onNameChange: (name: string) => void; + onBack: () => void; + onContinue: () => void; +}; + +export function OnboardingNameStep({ + config, + name, + onNameChange, + onBack, + onContinue, +}: OnboardingNameStepProps) { + const { t } = useLingui(); + const inputId = useId(); + const [draft, setDraft] = useState(name); + + function commitAndContinue() { + const trimmed = draft.trim() || DEFAULT_FIRST_OO_NAME; + onNameChange(trimmed); + onContinue(); + } + + return ( +
+

+ Name your Oo +

+

+ Something short and yours — or keep the default and rename later. +

+ +
+ +
+ + setDraft(event.target.value)} + onKeyDown={(event) => { + if (event.key === "Enter") commitAndContinue(); + }} + className="w-full rounded-2xl border-2 border-border bg-card px-4 py-3 text-lg font-semibold tracking-tight text-foreground shadow-sm outline-none transition-colors placeholder:text-muted-foreground/60 focus:border-primary" + /> +

+ {t`Press Enter or Continue — skip keeps ${DEFAULT_FIRST_OO_NAME}.`} +

+
+
+ +
+ +
+ + +
+
+
+ ); +} diff --git a/apps/web/src/components/onboarding/OnboardingVoiceStep.tsx b/apps/web/src/components/onboarding/OnboardingVoiceStep.tsx index 91cd569..34cd93b 100644 --- a/apps/web/src/components/onboarding/OnboardingVoiceStep.tsx +++ b/apps/web/src/components/onboarding/OnboardingVoiceStep.tsx @@ -1,15 +1,20 @@ import { Trans } from "@lingui/react/macro"; import type { VoiceInfo } from "@rakazo/contracts"; -import { defaultVoiceForLocale, voicesForLocale } from "@rakazo/core"; +import { voicesForLocale } from "@rakazo/core"; import { Button } from "@rakazo/ui-web"; import { useEffect, useRef, useState } from "react"; +import { + defaultOnboardingVoiceId, + offlineOnboardingVoices, +} from "../../lib/onboarding-voices"; import { rpc } from "../../lib/rpc"; -import { readUiLocale } from "../../lib/ui-locale"; +import type { UiLocale } from "../../lib/ui-locale"; /** Must match packages/adapters KokoroVoiceProvider placeholder. */ export const KOKORO_LOCAL_TOKEN = "local-kokoro"; type OnboardingVoiceStepProps = { + uiLocale: UiLocale; selectedVoiceId: string; onSelectedVoiceIdChange: (voiceId: string) => void; onBack: () => void; @@ -18,6 +23,7 @@ type OnboardingVoiceStepProps = { }; export function OnboardingVoiceStep({ + uiLocale, selectedVoiceId, onSelectedVoiceIdChange, onBack, @@ -36,13 +42,17 @@ export function OnboardingVoiceStep({ await rpc.voice.connect({ provider: "kokoro", apiKey: KOKORO_LOCAL_TOKEN }); const listed = await rpc.voice.voices({ provider: "kokoro" }); if (cancelled) return; - const locale = readUiLocale(); - const ordered = voicesForLocale(listed, locale).slice(0, 6); + const ordered = voicesForLocale(listed, uiLocale).slice(0, 6); setVoiceOptions(ordered); - const defaultVoice = defaultVoiceForLocale(listed, locale); - if (defaultVoice) onSelectedVoiceIdChange(defaultVoice.id); + const nextId = defaultOnboardingVoiceId(uiLocale, ordered); + if (nextId) onSelectedVoiceIdChange(nextId); } catch { - if (!cancelled) setVoiceOptions([]); + if (!cancelled) { + const fallback = offlineOnboardingVoices(uiLocale); + setVoiceOptions(fallback); + const nextId = defaultOnboardingVoiceId(uiLocale, fallback); + if (nextId) onSelectedVoiceIdChange(nextId); + } } finally { if (!cancelled) setVoiceLoading(false); } @@ -54,7 +64,7 @@ export function OnboardingVoiceStep({ return () => { cancelled = true; }; - }, [onSelectedVoiceIdChange]); + }, [onSelectedVoiceIdChange, uiLocale]); async function skipVoice() { await previewTask.current; diff --git a/apps/web/src/components/stage/OoStage.tsx b/apps/web/src/components/stage/OoStage.tsx index fc086f4..55f99ac 100644 --- a/apps/web/src/components/stage/OoStage.tsx +++ b/apps/web/src/components/stage/OoStage.tsx @@ -1,4 +1,5 @@ import { useLingui } from "@lingui/react/macro"; +import { isMultiOoGroupChat } from "../../lib/group-chat"; import type { ThreadSnapshot } from "@rakazo/contracts"; import { BotAvatar } from "@rakazo/ui-web"; import { useEffect, useMemo, useState } from "react"; @@ -317,6 +318,7 @@ export function OoGroupStage({ [snapshot, speaker, enrichMember], ); const roster = snapshot?.members ?? []; + const multiOo = isMultiOoGroupChat(roster.length); const groupSpark = useMemo( () => extractGroupSpark(snapshot, workingBots.length), [snapshot, workingBots.length], @@ -405,7 +407,7 @@ export function OoGroupStage({ if (sparkLabel && groupSpark?.kind === "peer-reply") return sparkLabel; if (awarenessLabel) return awarenessLabel; if (sparkLabel) return sparkLabel; - if (!speaker) return t`Group conversation`; + if (!speaker) return multiOo ? t`Group conversation` : t`1:1 conversation`; if (running && workingBots.length > 1) { return t`${workingBots.length} Oos are working — ${speaker.name} has the floor`; } @@ -429,8 +431,15 @@ export function OoGroupStage({ thinkingIndex, thinkingLines, workingBots.length, + multiOo, ]); + const headerSubtitle = multiOo + ? t`Group conversation` + : speaker + ? t`1:1 with ${speaker.name}` + : t`1:1 conversation`; + const energy = groupStageEnergy(phase, groupSpark, workingBots.length); const phaseLabel = showEnsembleOnly @@ -454,7 +463,7 @@ export function OoGroupStage({ return (
{groupName ?? t`Group`} -

{t`Group conversation`}

+

{headerSubtitle}

{awarenessLabel && awarenessEvent && speaker ? ( diff --git a/apps/web/src/lib/app-routes.ts b/apps/web/src/lib/app-routes.ts new file mode 100644 index 0000000..1fe8c3e --- /dev/null +++ b/apps/web/src/lib/app-routes.ts @@ -0,0 +1,5 @@ +export const HEUTE_APP_PATH = "/app/heute"; + +export function isHeuteAppPath(pathname: string): boolean { + return pathname === HEUTE_APP_PATH || pathname.endsWith(`${HEUTE_APP_PATH}`); +} diff --git a/apps/web/src/lib/group-chat.test.ts b/apps/web/src/lib/group-chat.test.ts new file mode 100644 index 0000000..d60f980 --- /dev/null +++ b/apps/web/src/lib/group-chat.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, it } from "vitest"; +import { isMultiOoGroupChat } from "./group-chat.js"; + +describe("isMultiOoGroupChat", () => { + it("treats a single Oo as 1:1", () => { + expect(isMultiOoGroupChat(0)).toBe(false); + expect(isMultiOoGroupChat(1)).toBe(false); + }); + + it("treats two or more Oos as a group", () => { + expect(isMultiOoGroupChat(2)).toBe(true); + expect(isMultiOoGroupChat(5)).toBe(true); + }); +}); diff --git a/apps/web/src/lib/group-chat.ts b/apps/web/src/lib/group-chat.ts new file mode 100644 index 0000000..8da80cf --- /dev/null +++ b/apps/web/src/lib/group-chat.ts @@ -0,0 +1,4 @@ +/** True when the encounter has more than one Oo (group chat), not a 1:1 thread. */ +export function isMultiOoGroupChat(memberCount: number): boolean { + return memberCount > 1; +} diff --git a/apps/web/src/lib/onboarding-voices.test.ts b/apps/web/src/lib/onboarding-voices.test.ts new file mode 100644 index 0000000..d10cea4 --- /dev/null +++ b/apps/web/src/lib/onboarding-voices.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from "vitest"; +import { defaultOnboardingVoiceId, offlineOnboardingVoices } from "./onboarding-voices.js"; + +describe("offlineOnboardingVoices", () => { + it("offers German voices for de", () => { + expect(offlineOnboardingVoices("de").map((voice) => voice.id)).toEqual([ + "df_eva", + "dm_bernd", + "df_victoria", + "dm_martin", + ]); + }); + + it("offers English voices for en", () => { + const ids = offlineOnboardingVoices("en").map((voice) => voice.id); + expect(ids).toContain("af_heart"); + expect(ids).not.toContain("df_eva"); + }); +}); + +describe("defaultOnboardingVoiceId", () => { + it("prefers df_eva for German", () => { + expect(defaultOnboardingVoiceId("de", offlineOnboardingVoices("de"))).toBe("df_eva"); + }); +}); diff --git a/apps/web/src/lib/onboarding-voices.ts b/apps/web/src/lib/onboarding-voices.ts new file mode 100644 index 0000000..694ced2 --- /dev/null +++ b/apps/web/src/lib/onboarding-voices.ts @@ -0,0 +1,23 @@ +import type { VoiceInfo } from "@rakazo/contracts"; +import { defaultVoiceForLocale, GERMAN_VOICE_PRESETS, voicesForLocale } from "@rakazo/core"; + +/** English presets when the local voice server is unreachable during onboarding. */ +const ENGLISH_ONBOARDING_VOICES: VoiceInfo[] = [ + { id: "af_heart", label: "Heart", description: "warm" }, + { id: "af_bella", label: "Bella", description: "expressive" }, + { id: "af_sky", label: "Sky", description: "bright" }, + { id: "af_nova", label: "Nova", description: "energetic" }, + { id: "am_adam", label: "Adam", description: "clear" }, + { id: "am_michael", label: "Michael", description: "steady" }, +]; + +const OFFLINE_CATALOG = [...GERMAN_VOICE_PRESETS, ...ENGLISH_ONBOARDING_VOICES]; + +/** Locale-aware voice choices for onboarding when listing from the server fails. */ +export function offlineOnboardingVoices(locale: string): VoiceInfo[] { + return voicesForLocale(OFFLINE_CATALOG, locale).slice(0, 6); +} + +export function defaultOnboardingVoiceId(locale: string, voices: VoiceInfo[]): string { + return defaultVoiceForLocale(voices, locale)?.id ?? ""; +} diff --git a/apps/web/src/locales/de/messages.po b/apps/web/src/locales/de/messages.po index 75034c2..f65f2db 100644 --- a/apps/web/src/locales/de/messages.po +++ b/apps/web/src/locales/de/messages.po @@ -303,6 +303,13 @@ msgstr "1:1-Unterhaltung" msgid "1:1 conversation with {title}" msgstr "1:1-Unterhaltung mit {title}" +#. placeholder {0}: mainConversationEntry.bot.name +#. placeholder {0}: speaker.name +#: src/components/stage/OoStage.tsx +#: src/pages/Shell.tsx +msgid "1:1 with {0}" +msgstr "1:1 mit {0}" + #: src/pages/shell/bot-picker.tsx msgid "About groups" msgstr "Über Gruppen" @@ -783,6 +790,7 @@ msgstr "Verfügbar, sobald die Routine gespeichert ist" #: src/components/browser/InAppBrowser.tsx #: src/components/CookingMode.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/pages/Onboarding.tsx #: src/pages/PluginsOverlay.tsx @@ -1423,6 +1431,7 @@ msgid "Context limit" msgstr "Kontextlimit" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingOoDesigner.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx @@ -3317,6 +3326,10 @@ msgstr "Gruppe benennen" msgid "Name this routine" msgstr "Routine benennen" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Name your Oo" +msgstr "Gib deinem Oo einen Namen" + #: src/components/OoAtelier.tsx msgid "Neck" msgstr "Hals" @@ -3627,6 +3640,10 @@ msgstr "Nur leere Spaces können gelöscht werden. Lösche zuerst die Bots und G msgid "Only file and folder names are read." msgstr "Gelesen werden nur Datei- und Ordnernamen." +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Oo name" +msgstr "Name des Oo" + #: src/pages/ScratchpadSection.tsx #: src/pages/shell/computer-panel-preview.tsx msgid "Open" @@ -3833,6 +3850,10 @@ msgstr "Vorlieben" msgid "Present" msgstr "Anwesend" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." +msgstr "Enter oder Weiter — Überspringen behält Chief." + #. placeholder {0}: props.name #: src/components/ArtifactFileCard.tsx msgid "Preview {0}" @@ -4574,6 +4595,7 @@ msgid "Skills" msgstr "Skills" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/lib/onboarding-copy.ts @@ -4581,9 +4603,10 @@ msgstr "Skills" msgid "Skip" msgstr "Überspringen" +#. placeholder {0}: UI_LOCALE_LABELS[activeUiLocale] #: src/pages/Onboarding.tsx -msgid "Skip · English" -msgstr "Überspringen · Englisch" +msgid "Skip · {0}" +msgstr "Überspringen · {0}" #: src/lib/localized-provider-hint.ts msgid "Skip or deploy key" @@ -4614,6 +4637,10 @@ msgstr "Software-Update" msgid "Something needs your attention." msgstr "Etwas braucht deine Aufmerksamkeit." +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Something short and yours — or keep the default and rename later." +msgstr "Kurz und persönlich — oder Standard behalten und später umbenennen." + #: src/lib/voice-persona-copy.ts msgid "Sorting priorities…" msgstr "Sortiere die Prioritäten…" diff --git a/apps/web/src/locales/en/messages.po b/apps/web/src/locales/en/messages.po index 3f24034..4715b6b 100644 --- a/apps/web/src/locales/en/messages.po +++ b/apps/web/src/locales/en/messages.po @@ -303,6 +303,13 @@ msgstr "1:1 conversation" msgid "1:1 conversation with {title}" msgstr "1:1 conversation with {title}" +#. placeholder {0}: mainConversationEntry.bot.name +#. placeholder {0}: speaker.name +#: src/components/stage/OoStage.tsx +#: src/pages/Shell.tsx +msgid "1:1 with {0}" +msgstr "1:1 with {0}" + #: src/pages/shell/bot-picker.tsx msgid "About groups" msgstr "About groups" @@ -783,6 +790,7 @@ msgstr "Available after the routine is saved" #: src/components/browser/InAppBrowser.tsx #: src/components/CookingMode.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/pages/Onboarding.tsx #: src/pages/PluginsOverlay.tsx @@ -1423,6 +1431,7 @@ msgid "Context limit" msgstr "Context limit" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingOoDesigner.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx @@ -3317,6 +3326,10 @@ msgstr "Name this group" msgid "Name this routine" msgstr "Name this routine" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Name your Oo" +msgstr "Name your Oo" + #: src/components/OoAtelier.tsx msgid "Neck" msgstr "Neck" @@ -3627,6 +3640,10 @@ msgstr "Only empty spaces can be deleted. Delete its bots and groups first." msgid "Only file and folder names are read." msgstr "Only file and folder names are read." +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Oo name" +msgstr "Oo name" + #: src/pages/ScratchpadSection.tsx #: src/pages/shell/computer-panel-preview.tsx msgid "Open" @@ -3833,6 +3850,10 @@ msgstr "Preferences" msgid "Present" msgstr "Present" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." +msgstr "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." + #. placeholder {0}: props.name #: src/components/ArtifactFileCard.tsx msgid "Preview {0}" @@ -4574,6 +4595,7 @@ msgid "Skills" msgstr "Skills" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/lib/onboarding-copy.ts @@ -4581,9 +4603,10 @@ msgstr "Skills" msgid "Skip" msgstr "Skip" +#. placeholder {0}: UI_LOCALE_LABELS[activeUiLocale] #: src/pages/Onboarding.tsx -msgid "Skip · English" -msgstr "Skip · English" +msgid "Skip · {0}" +msgstr "Skip · {0}" #: src/lib/localized-provider-hint.ts msgid "Skip or deploy key" @@ -4614,6 +4637,10 @@ msgstr "Software update" msgid "Something needs your attention." msgstr "Something needs your attention." +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Something short and yours — or keep the default and rename later." +msgstr "Something short and yours — or keep the default and rename later." + #: src/lib/voice-persona-copy.ts msgid "Sorting priorities…" msgstr "Sorting priorities…" diff --git a/apps/web/src/locales/es/messages.po b/apps/web/src/locales/es/messages.po index e7f6a10..ca5c21f 100644 --- a/apps/web/src/locales/es/messages.po +++ b/apps/web/src/locales/es/messages.po @@ -303,6 +303,13 @@ msgstr "" msgid "1:1 conversation with {title}" msgstr "" +#. placeholder {0}: mainConversationEntry.bot.name +#. placeholder {0}: speaker.name +#: src/components/stage/OoStage.tsx +#: src/pages/Shell.tsx +msgid "1:1 with {0}" +msgstr "" + #: src/pages/shell/bot-picker.tsx msgid "About groups" msgstr "" @@ -783,6 +790,7 @@ msgstr "Disponible después de guardar la rutina" #: src/components/browser/InAppBrowser.tsx #: src/components/CookingMode.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/pages/Onboarding.tsx #: src/pages/PluginsOverlay.tsx @@ -1423,6 +1431,7 @@ msgid "Context limit" msgstr "Límite de contexto" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingOoDesigner.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx @@ -3317,6 +3326,10 @@ msgstr "Nombra este grupo" msgid "Name this routine" msgstr "Nombra esta rutina" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Name your Oo" +msgstr "" + #: src/components/OoAtelier.tsx msgid "Neck" msgstr "" @@ -3627,6 +3640,10 @@ msgstr "" msgid "Only file and folder names are read." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Oo name" +msgstr "" + #: src/pages/ScratchpadSection.tsx #: src/pages/shell/computer-panel-preview.tsx msgid "Open" @@ -3833,6 +3850,10 @@ msgstr "" msgid "Present" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." +msgstr "" + #. placeholder {0}: props.name #: src/components/ArtifactFileCard.tsx msgid "Preview {0}" @@ -4574,6 +4595,7 @@ msgid "Skills" msgstr "Habilidades" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/lib/onboarding-copy.ts @@ -4581,8 +4603,9 @@ msgstr "Habilidades" msgid "Skip" msgstr "Omitir" +#. placeholder {0}: UI_LOCALE_LABELS[activeUiLocale] #: src/pages/Onboarding.tsx -msgid "Skip · English" +msgid "Skip · {0}" msgstr "" #: src/lib/localized-provider-hint.ts @@ -4614,6 +4637,10 @@ msgstr "Actualización de software" msgid "Something needs your attention." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Something short and yours — or keep the default and rename later." +msgstr "" + #: src/lib/voice-persona-copy.ts msgid "Sorting priorities…" msgstr "" diff --git a/apps/web/src/locales/hi/messages.po b/apps/web/src/locales/hi/messages.po index c66c048..11b0f05 100644 --- a/apps/web/src/locales/hi/messages.po +++ b/apps/web/src/locales/hi/messages.po @@ -303,6 +303,13 @@ msgstr "" msgid "1:1 conversation with {title}" msgstr "" +#. placeholder {0}: mainConversationEntry.bot.name +#. placeholder {0}: speaker.name +#: src/components/stage/OoStage.tsx +#: src/pages/Shell.tsx +msgid "1:1 with {0}" +msgstr "" + #: src/pages/shell/bot-picker.tsx msgid "About groups" msgstr "" @@ -783,6 +790,7 @@ msgstr "" #: src/components/browser/InAppBrowser.tsx #: src/components/CookingMode.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/pages/Onboarding.tsx #: src/pages/PluginsOverlay.tsx @@ -1423,6 +1431,7 @@ msgid "Context limit" msgstr "कॉन्टेक्स्ट सीमा" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingOoDesigner.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx @@ -3317,6 +3326,10 @@ msgstr "इस समूह को नाम दें" msgid "Name this routine" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Name your Oo" +msgstr "" + #: src/components/OoAtelier.tsx msgid "Neck" msgstr "" @@ -3627,6 +3640,10 @@ msgstr "" msgid "Only file and folder names are read." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Oo name" +msgstr "" + #: src/pages/ScratchpadSection.tsx #: src/pages/shell/computer-panel-preview.tsx msgid "Open" @@ -3833,6 +3850,10 @@ msgstr "" msgid "Present" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." +msgstr "" + #. placeholder {0}: props.name #: src/components/ArtifactFileCard.tsx msgid "Preview {0}" @@ -4574,6 +4595,7 @@ msgid "Skills" msgstr "स्किल्स" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/lib/onboarding-copy.ts @@ -4581,8 +4603,9 @@ msgstr "स्किल्स" msgid "Skip" msgstr "छोड़ें" +#. placeholder {0}: UI_LOCALE_LABELS[activeUiLocale] #: src/pages/Onboarding.tsx -msgid "Skip · English" +msgid "Skip · {0}" msgstr "" #: src/lib/localized-provider-hint.ts @@ -4614,6 +4637,10 @@ msgstr "" msgid "Something needs your attention." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Something short and yours — or keep the default and rename later." +msgstr "" + #: src/lib/voice-persona-copy.ts msgid "Sorting priorities…" msgstr "" diff --git a/apps/web/src/locales/ko/messages.po b/apps/web/src/locales/ko/messages.po index f3010f1..b4b731e 100644 --- a/apps/web/src/locales/ko/messages.po +++ b/apps/web/src/locales/ko/messages.po @@ -303,6 +303,13 @@ msgstr "" msgid "1:1 conversation with {title}" msgstr "" +#. placeholder {0}: mainConversationEntry.bot.name +#. placeholder {0}: speaker.name +#: src/components/stage/OoStage.tsx +#: src/pages/Shell.tsx +msgid "1:1 with {0}" +msgstr "" + #: src/pages/shell/bot-picker.tsx msgid "About groups" msgstr "" @@ -783,6 +790,7 @@ msgstr "루틴을 저장한 후 사용할 수 있습니다" #: src/components/browser/InAppBrowser.tsx #: src/components/CookingMode.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/pages/Onboarding.tsx #: src/pages/PluginsOverlay.tsx @@ -1423,6 +1431,7 @@ msgid "Context limit" msgstr "컨텍스트 한도" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingOoDesigner.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx @@ -3317,6 +3326,10 @@ msgstr "그룹 이름" msgid "Name this routine" msgstr "이 루틴의 이름을 정하세요" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Name your Oo" +msgstr "" + #: src/components/OoAtelier.tsx msgid "Neck" msgstr "" @@ -3627,6 +3640,10 @@ msgstr "" msgid "Only file and folder names are read." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Oo name" +msgstr "" + #: src/pages/ScratchpadSection.tsx #: src/pages/shell/computer-panel-preview.tsx msgid "Open" @@ -3833,6 +3850,10 @@ msgstr "" msgid "Present" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." +msgstr "" + #. placeholder {0}: props.name #: src/components/ArtifactFileCard.tsx msgid "Preview {0}" @@ -4574,6 +4595,7 @@ msgid "Skills" msgstr "스킬" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/lib/onboarding-copy.ts @@ -4581,8 +4603,9 @@ msgstr "스킬" msgid "Skip" msgstr "건너뛰기" +#. placeholder {0}: UI_LOCALE_LABELS[activeUiLocale] #: src/pages/Onboarding.tsx -msgid "Skip · English" +msgid "Skip · {0}" msgstr "" #: src/lib/localized-provider-hint.ts @@ -4614,6 +4637,10 @@ msgstr "소프트웨어 업데이트" msgid "Something needs your attention." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Something short and yours — or keep the default and rename later." +msgstr "" + #: src/lib/voice-persona-copy.ts msgid "Sorting priorities…" msgstr "" diff --git a/apps/web/src/locales/pt-BR/messages.po b/apps/web/src/locales/pt-BR/messages.po index 55318d3..443bc2b 100644 --- a/apps/web/src/locales/pt-BR/messages.po +++ b/apps/web/src/locales/pt-BR/messages.po @@ -303,6 +303,13 @@ msgstr "" msgid "1:1 conversation with {title}" msgstr "" +#. placeholder {0}: mainConversationEntry.bot.name +#. placeholder {0}: speaker.name +#: src/components/stage/OoStage.tsx +#: src/pages/Shell.tsx +msgid "1:1 with {0}" +msgstr "" + #: src/pages/shell/bot-picker.tsx msgid "About groups" msgstr "" @@ -783,6 +790,7 @@ msgstr "" #: src/components/browser/InAppBrowser.tsx #: src/components/CookingMode.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/pages/Onboarding.tsx #: src/pages/PluginsOverlay.tsx @@ -1423,6 +1431,7 @@ msgid "Context limit" msgstr "Limite de contexto" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingOoDesigner.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx @@ -3317,6 +3326,10 @@ msgstr "Dar nome ao grupo" msgid "Name this routine" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Name your Oo" +msgstr "" + #: src/components/OoAtelier.tsx msgid "Neck" msgstr "" @@ -3627,6 +3640,10 @@ msgstr "" msgid "Only file and folder names are read." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Oo name" +msgstr "" + #: src/pages/ScratchpadSection.tsx #: src/pages/shell/computer-panel-preview.tsx msgid "Open" @@ -3833,6 +3850,10 @@ msgstr "" msgid "Present" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." +msgstr "" + #. placeholder {0}: props.name #: src/components/ArtifactFileCard.tsx msgid "Preview {0}" @@ -4574,6 +4595,7 @@ msgid "Skills" msgstr "Habilidades" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/lib/onboarding-copy.ts @@ -4581,8 +4603,9 @@ msgstr "Habilidades" msgid "Skip" msgstr "Pular" +#. placeholder {0}: UI_LOCALE_LABELS[activeUiLocale] #: src/pages/Onboarding.tsx -msgid "Skip · English" +msgid "Skip · {0}" msgstr "" #: src/lib/localized-provider-hint.ts @@ -4614,6 +4637,10 @@ msgstr "" msgid "Something needs your attention." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Something short and yours — or keep the default and rename later." +msgstr "" + #: src/lib/voice-persona-copy.ts msgid "Sorting priorities…" msgstr "" diff --git a/apps/web/src/locales/ru/messages.po b/apps/web/src/locales/ru/messages.po index a605f54..b504d10 100644 --- a/apps/web/src/locales/ru/messages.po +++ b/apps/web/src/locales/ru/messages.po @@ -303,6 +303,13 @@ msgstr "" msgid "1:1 conversation with {title}" msgstr "" +#. placeholder {0}: mainConversationEntry.bot.name +#. placeholder {0}: speaker.name +#: src/components/stage/OoStage.tsx +#: src/pages/Shell.tsx +msgid "1:1 with {0}" +msgstr "" + #: src/pages/shell/bot-picker.tsx msgid "About groups" msgstr "О группах" @@ -783,6 +790,7 @@ msgstr "Доступно после сохранения задачи" #: src/components/browser/InAppBrowser.tsx #: src/components/CookingMode.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/pages/Onboarding.tsx #: src/pages/PluginsOverlay.tsx @@ -1423,6 +1431,7 @@ msgid "Context limit" msgstr "" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingOoDesigner.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx @@ -3317,6 +3326,10 @@ msgstr "Назовите эту группу" msgid "Name this routine" msgstr "Назовите эту задачу" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Name your Oo" +msgstr "" + #: src/components/OoAtelier.tsx msgid "Neck" msgstr "" @@ -3627,6 +3640,10 @@ msgstr "Удалить можно только пустые пространст msgid "Only file and folder names are read." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Oo name" +msgstr "" + #: src/pages/ScratchpadSection.tsx #: src/pages/shell/computer-panel-preview.tsx msgid "Open" @@ -3833,6 +3850,10 @@ msgstr "" msgid "Present" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." +msgstr "" + #. placeholder {0}: props.name #: src/components/ArtifactFileCard.tsx msgid "Preview {0}" @@ -4574,6 +4595,7 @@ msgid "Skills" msgstr "Навыки" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/lib/onboarding-copy.ts @@ -4581,8 +4603,9 @@ msgstr "Навыки" msgid "Skip" msgstr "Пропустить" +#. placeholder {0}: UI_LOCALE_LABELS[activeUiLocale] #: src/pages/Onboarding.tsx -msgid "Skip · English" +msgid "Skip · {0}" msgstr "" #: src/lib/localized-provider-hint.ts @@ -4614,6 +4637,10 @@ msgstr "Обновление программного обеспечения" msgid "Something needs your attention." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Something short and yours — or keep the default and rename later." +msgstr "" + #: src/lib/voice-persona-copy.ts msgid "Sorting priorities…" msgstr "" diff --git a/apps/web/src/locales/tr/messages.po b/apps/web/src/locales/tr/messages.po index cf96268..266f929 100644 --- a/apps/web/src/locales/tr/messages.po +++ b/apps/web/src/locales/tr/messages.po @@ -303,6 +303,13 @@ msgstr "" msgid "1:1 conversation with {title}" msgstr "" +#. placeholder {0}: mainConversationEntry.bot.name +#. placeholder {0}: speaker.name +#: src/components/stage/OoStage.tsx +#: src/pages/Shell.tsx +msgid "1:1 with {0}" +msgstr "" + #: src/pages/shell/bot-picker.tsx msgid "About groups" msgstr "" @@ -783,6 +790,7 @@ msgstr "" #: src/components/browser/InAppBrowser.tsx #: src/components/CookingMode.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/pages/Onboarding.tsx #: src/pages/PluginsOverlay.tsx @@ -1423,6 +1431,7 @@ msgid "Context limit" msgstr "Bağlam sınırı" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingOoDesigner.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx @@ -3317,6 +3326,10 @@ msgstr "Gruba bir ad ver" msgid "Name this routine" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Name your Oo" +msgstr "" + #: src/components/OoAtelier.tsx msgid "Neck" msgstr "" @@ -3627,6 +3640,10 @@ msgstr "" msgid "Only file and folder names are read." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Oo name" +msgstr "" + #: src/pages/ScratchpadSection.tsx #: src/pages/shell/computer-panel-preview.tsx msgid "Open" @@ -3833,6 +3850,10 @@ msgstr "" msgid "Present" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." +msgstr "" + #. placeholder {0}: props.name #: src/components/ArtifactFileCard.tsx msgid "Preview {0}" @@ -4574,6 +4595,7 @@ msgid "Skills" msgstr "Beceriler" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/lib/onboarding-copy.ts @@ -4581,8 +4603,9 @@ msgstr "Beceriler" msgid "Skip" msgstr "Atla" +#. placeholder {0}: UI_LOCALE_LABELS[activeUiLocale] #: src/pages/Onboarding.tsx -msgid "Skip · English" +msgid "Skip · {0}" msgstr "" #: src/lib/localized-provider-hint.ts @@ -4614,6 +4637,10 @@ msgstr "" msgid "Something needs your attention." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Something short and yours — or keep the default and rename later." +msgstr "" + #: src/lib/voice-persona-copy.ts msgid "Sorting priorities…" msgstr "" diff --git a/apps/web/src/locales/zh-CN/messages.po b/apps/web/src/locales/zh-CN/messages.po index c7ce6a5..ea60fd4 100644 --- a/apps/web/src/locales/zh-CN/messages.po +++ b/apps/web/src/locales/zh-CN/messages.po @@ -303,6 +303,13 @@ msgstr "" msgid "1:1 conversation with {title}" msgstr "" +#. placeholder {0}: mainConversationEntry.bot.name +#. placeholder {0}: speaker.name +#: src/components/stage/OoStage.tsx +#: src/pages/Shell.tsx +msgid "1:1 with {0}" +msgstr "" + #: src/pages/shell/bot-picker.tsx msgid "About groups" msgstr "" @@ -783,6 +790,7 @@ msgstr "保存例行任务后可用" #: src/components/browser/InAppBrowser.tsx #: src/components/CookingMode.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/pages/Onboarding.tsx #: src/pages/PluginsOverlay.tsx @@ -1423,6 +1431,7 @@ msgid "Context limit" msgstr "上下文限制" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingOoDesigner.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx @@ -3317,6 +3326,10 @@ msgstr "给这个群组命名" msgid "Name this routine" msgstr "给这个例行任务命名" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Name your Oo" +msgstr "" + #: src/components/OoAtelier.tsx msgid "Neck" msgstr "" @@ -3627,6 +3640,10 @@ msgstr "" msgid "Only file and folder names are read." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Oo name" +msgstr "" + #: src/pages/ScratchpadSection.tsx #: src/pages/shell/computer-panel-preview.tsx msgid "Open" @@ -3833,6 +3850,10 @@ msgstr "" msgid "Present" msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Press Enter or Continue — skip keeps {DEFAULT_FIRST_OO_NAME}." +msgstr "" + #. placeholder {0}: props.name #: src/components/ArtifactFileCard.tsx msgid "Preview {0}" @@ -4574,6 +4595,7 @@ msgid "Skills" msgstr "技能" #: src/components/integrations/IntegrationSetup.tsx +#: src/components/onboarding/OnboardingNameStep.tsx #: src/components/onboarding/OnboardingScanStep.tsx #: src/components/onboarding/OnboardingVoiceStep.tsx #: src/lib/onboarding-copy.ts @@ -4581,8 +4603,9 @@ msgstr "技能" msgid "Skip" msgstr "跳过" +#. placeholder {0}: UI_LOCALE_LABELS[activeUiLocale] #: src/pages/Onboarding.tsx -msgid "Skip · English" +msgid "Skip · {0}" msgstr "" #: src/lib/localized-provider-hint.ts @@ -4614,6 +4637,10 @@ msgstr "软件更新" msgid "Something needs your attention." msgstr "" +#: src/components/onboarding/OnboardingNameStep.tsx +msgid "Something short and yours — or keep the default and rename later." +msgstr "" + #: src/lib/voice-persona-copy.ts msgid "Sorting priorities…" msgstr "" diff --git a/apps/web/src/pages/Onboarding.tsx b/apps/web/src/pages/Onboarding.tsx index bdbc2f2..1b41bdf 100644 --- a/apps/web/src/pages/Onboarding.tsx +++ b/apps/web/src/pages/Onboarding.tsx @@ -5,6 +5,10 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; import { IntegrationSetup } from "../components/integrations/IntegrationSetup"; import { OnboardingDiscoveryShell } from "../components/onboarding/OnboardingDiscoveryShell"; +import { + DEFAULT_FIRST_OO_NAME, + OnboardingNameStep, +} from "../components/onboarding/OnboardingNameStep"; import { type OnboardingEmojiConfig, OnboardingOoDesigner, @@ -23,7 +27,12 @@ import { import { findLookCategory, lookPresetToAvatarValue } from "../lib/onboarding-look-categories"; import { onboardingProfileUpdate } from "../lib/onboarding-profile"; import { rpc, selectSpace } from "../lib/rpc"; -import { persistUiLocale, UI_LOCALE_LABELS, type UiLocale } from "../lib/ui-locale"; +import { + persistUiLocale, + resolveUiLocale, + UI_LOCALE_LABELS, + type UiLocale, +} from "../lib/ui-locale"; import { collectLocalProfileSignals, persistUserProfileHarvest } from "../lib/user-profile-harvest"; /** Selects the main conversation's space and returns its route. */ @@ -44,8 +53,9 @@ export function OnboardingPage() { const { t } = useLingui(); const navigate = useNavigate(); const [step, setStep] = useState< - "loading" | "emoji" | "language" | "voice" | "integrations" | "scan" | "bot" + "loading" | "emoji" | "name" | "language" | "voice" | "integrations" | "scan" | "bot" >("emoji"); + const [ooName, setOoName] = useState(DEFAULT_FIRST_OO_NAME); const [selectedVoiceId, setSelectedVoiceId] = useState(""); const [voiceSkipped, setVoiceSkipped] = useState(false); const [integrationSetup, setIntegrationSetup] = useState(null); @@ -57,14 +67,9 @@ export function OnboardingPage() { const [crewReady, setCrewReady] = useState(false); const [emojiConfig, setEmojiConfig] = useState(defaultEmojiConfig); - const [currentLocale, setCurrentLocale] = useState(() => { - try { - return localStorage.getItem("rakazo.uiLocale") ?? "en"; - } catch { - return "en"; - } - }); + const [currentLocale, setCurrentLocale] = useState(() => resolveUiLocale()); const [languageChoice, setLanguageChoice] = useState(null); + const activeUiLocale = languageChoice ?? currentLocale; useEffect(() => { let cancelled = false; @@ -110,7 +115,7 @@ export function OnboardingPage() { const discoveryPhase: OnboardingDiscoveryPhase = step === "loading" ? "loading" - : step === "emoji" || step === "language" || step === "voice" + : step === "emoji" || step === "name" || step === "language" || step === "voice" ? "emoji" : step === "integrations" ? "integrations" @@ -145,7 +150,10 @@ export function OnboardingPage() { setError(null); try { const bot = await rpc.bots.ensureMain(); - await rpc.bots.update({ botId: bot.id, color: emojiConfig.avatarValue }).catch(() => null); + const trimmedName = ooName.trim() || DEFAULT_FIRST_OO_NAME; + await rpc.bots + .update({ botId: bot.id, color: emojiConfig.avatarValue, name: trimmedName }) + .catch(() => null); setCrewReady(true); await rpc.profile @@ -213,7 +221,11 @@ export function OnboardingPage() { {error && step !== "bot" ?

{error}

: null} @@ -222,6 +234,16 @@ export function OnboardingPage() { setStep("name")} + /> + ) : null} + + {step === "name" ? ( + setStep("emoji")} onContinue={() => setStep("language")} /> ) : null} @@ -241,7 +263,7 @@ export function OnboardingPage() { type="button" onClick={() => { persistUiLocale(code as UiLocale); - setCurrentLocale(code); + setCurrentLocale(code as UiLocale); setLanguageChoice(code as UiLocale); setStep("voice"); }} @@ -256,11 +278,18 @@ export function OnboardingPage() { ))}
- -
@@ -268,6 +297,7 @@ export function OnboardingPage() { {step === "voice" ? ( setStep("language")} diff --git a/apps/web/src/pages/Shell.tsx b/apps/web/src/pages/Shell.tsx index ddec27f..1cda86b 100644 --- a/apps/web/src/pages/Shell.tsx +++ b/apps/web/src/pages/Shell.tsx @@ -84,7 +84,9 @@ import { useState, } from "react"; import { createPortal } from "react-dom"; -import { useNavigate, useParams, useSearchParams } from "react-router-dom"; +import { useLocation, useNavigate, useParams, useSearchParams } from "react-router-dom"; +import { HEUTE_APP_PATH, isHeuteAppPath } from "../lib/app-routes"; +import { isMultiOoGroupChat } from "../lib/group-chat"; import { ArtifactPane } from "../components/ArtifactPane"; import { InAppBrowser } from "../components/browser/InAppBrowser"; import { ComputerUpdateProgress } from "../components/ComputerUpdateProgress"; @@ -267,14 +269,21 @@ import { SortableGroup } from "../components/SortableGroup"; export function ShellPage() { const { t } = useLingui(); const { botId, groupId } = useParams(); + const location = useLocation(); const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); + const onHeuteRoute = isHeuteAppPath(location.pathname); const [activeNavPillar, setActiveNavPillar] = useState(() => { + if (isHeuteAppPath(location.pathname)) return "heute"; if (groupId) return "projekte"; if (botId) return "team"; return "heute"; }); + useEffect(() => { + if (isHeuteAppPath(location.pathname)) setActiveNavPillar("heute"); + }, [location.pathname]); + useEffect(() => { if (botId) setActiveNavPillar("team"); }, [botId]); @@ -611,6 +620,8 @@ export function ShellPage() { const recordingSkill = activeTaughtSkills.find((skill) => skill.status === "recording") ?? null; const routeBotId = useRef(botId); routeBotId.current = botId; + const routeOnHeute = useRef(onHeuteRoute); + routeOnHeute.current = onHeuteRoute; const routeGroupId = useRef(groupId); routeGroupId.current = groupId; const activeBotId = useRef(inGroup ? undefined : active?.id); @@ -745,6 +756,7 @@ export function ShellPage() { return; } if (!currentBotId || !list.some((bot) => bot.id === currentBotId)) { + if (routeOnHeute.current) return; navigate(firstThreadRoute(list, groupList, inProject), { replace: true }); } } finally { @@ -1580,6 +1592,9 @@ export function ShellPage() { : snapshot?.botId === active?.id ? snapshot : null; + const activeGroupMemberCount = + activeGroup?.members?.length ?? activeSnapshot?.members?.length ?? 0; + const multiOoInGroup = inGroup && isMultiOoGroupChat(activeGroupMemberCount); const activeReplyTarget = replyTarget && activeSnapshot?.messages.some((message) => message.id === replyTarget.id) ? replyTarget @@ -2796,6 +2811,7 @@ export function ShellPage() { ooSound.play("interact"); setActiveNavPillar("heute"); setMobileSidebarOpen(false); + navigate(HEUTE_APP_PATH); }} className={rosterRowClass(activeNavPillar === "heute")} > @@ -3213,10 +3229,17 @@ export function ShellPage() { {composerRunning || active.status === "running" ? t`Busy` : active.status} - ) : inGroup && projectSessionKey ? ( + ) : multiOoInGroup && projectSessionKey ? ( {t`Group conversation`} + ) : inGroup && + activeGroup?.kind === "main" && + activeGroupMemberCount <= 1 && + mainConversationEntry ? ( + + {t`1:1 with ${mainConversationEntry.bot.name}`} + ) : inGroup && activeGroup?.kind !== "main" ? ( {t`Project · sessions & folders`} diff --git a/apps/web/src/pages/shell/sidebar-toggle-buttons.tsx b/apps/web/src/pages/shell/sidebar-toggle-buttons.tsx index fdbd5f8..7186be5 100644 --- a/apps/web/src/pages/shell/sidebar-toggle-buttons.tsx +++ b/apps/web/src/pages/shell/sidebar-toggle-buttons.tsx @@ -20,6 +20,7 @@ export function SidebarToggleButtons({ {collapsed && desktopBridge() ? : null}