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() {
}
/>
+
+ {t`Press Enter or Continue — skip keeps ${DEFAULT_FIRST_OO_NAME}.`} +
+{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{error}
: null} @@ -222,6 +234,16 @@ export function OnboardingPage() {