Show About you inside General settings (#165)

The profile had its own settings tab although it is a few rows of personal
details. It now sits as one card under Account in General, hides itself
while empty, and the separate tab is gone.


Claude-Session: https://claude.ai/code/session_017gHYZ4c9Twex73v4ZasmMJ

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
cynarAI 2026-10-05 18:36:54 +02:00 committed by GitHub
parent 23d70971f8
commit 3291a32d37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 55 additions and 97 deletions

View File

@ -245,7 +245,6 @@ export async function openUserSettings(
| "integrations" | "integrations"
| "models" | "models"
| "memory" | "memory"
| "profile"
| "voice" | "voice"
| "notifications" | "notifications"
| "usage" | "usage"

View File

@ -1,5 +1,5 @@
/** /**
* E2E: Settings → About you edits a fact in place: its wording, category and the spaces it * E2E: Settings → General → About you edits a fact in place: its wording, category and the spaces it
* shows in. * shows in.
*/ */
import { expect, test } from "@playwright/test"; import { expect, test } from "@playwright/test";
@ -15,7 +15,7 @@ test("a fact's wording, category and spaces are edited in place", async ({ page
addFacts: [{ category: "preferences", value: "Prefers tea" }], addFacts: [{ category: "preferences", value: "Prefers tea" }],
}); });
const settings = await openUserSettings(page, "profile"); const settings = await openUserSettings(page);
const profile = settings.getByTestId("user-profile-settings"); const profile = settings.getByTestId("user-profile-settings");
const tea = profile.getByTestId("user-fact").filter({ hasText: "Prefers tea" }); const tea = profile.getByTestId("user-fact").filter({ hasText: "Prefers tea" });
await tea.getByRole("button", { name: "Edit" }).click(); await tea.getByRole("button", { name: "Edit" }).click();

View File

@ -1,6 +1,6 @@
/** /**
* E2E: onboarding fills the user profile without asking (time zone, locale and language from * E2E: onboarding fills the user profile without asking (time zone, locale and language from
* the browser) and records the focus answer as a fact; Settings → About you shows every fact * the browser) and records the focus answer as a fact; Settings → General → About you shows every fact
* with its origin, confirms a pending one, and forgets one or all of them. * with its origin, confirms a pending one, and forgets one or all of them.
*/ */
import { expect, test } from "@playwright/test"; import { expect, test } from "@playwright/test";
@ -58,8 +58,8 @@ test("onboarding feeds the profile; facts can be confirmed and forgotten", async
addFacts: [{ category: "health", value: "Allergic to peanuts", source: "onboarding" }], addFacts: [{ category: "health", value: "Allergic to peanuts", source: "onboarding" }],
}); });
const settings = await openUserSettings(page, "profile"); const settings = await openUserSettings(page);
await expect(settings).toHaveAttribute("data-settings-section", "profile"); await expect(settings).toHaveAttribute("data-settings-section", "general");
const profile = settings.getByTestId("user-profile-settings"); const profile = settings.getByTestId("user-profile-settings");
await expect(profile.getByTestId("profile-field-timeZone")).toContainText("Europe/Vienna"); await expect(profile.getByTestId("profile-field-timeZone")).toContainText("Europe/Vienna");
await expect(profile.getByTestId("profile-field-language")).toContainText("English"); await expect(profile.getByTestId("profile-field-language")).toContainText("English");
@ -99,6 +99,6 @@ test("onboarding feeds the profile; facts can be confirmed and forgotten", async
await profile.getByRole("button", { name: "Forget everything" }).click(); await profile.getByRole("button", { name: "Forget everything" }).click();
await page.getByRole("alertdialog").getByRole("button", { name: "Forget", exact: true }).click(); await page.getByRole("alertdialog").getByRole("button", { name: "Forget", exact: true }).click();
await expect(profile).toContainText("Nothing yet"); await expect(profile).toHaveCount(0);
expect(await rpc<{ facts: unknown[] }>(page, "profile/get", null)).toMatchObject({ facts: [] }); expect(await rpc<{ facts: unknown[] }>(page, "profile/get", null)).toMatchObject({ facts: [] });
}); });

View File

@ -151,54 +151,51 @@ export function UserProfileSettings() {
</Button> </Button>
); );
if (!error && (!state || (fields.length === 0 && groups.length === 0))) return null;
return ( return (
<div data-testid="user-profile-settings" className="space-y-5"> <section
data-testid="user-profile-settings"
className="space-y-4 rounded-xl border border-border px-4 py-4"
>
<h3 className="text-ui-body font-medium text-foreground">
<Trans>About you</Trans>
</h3>
{error ? ( {error ? (
<p role="alert" className="text-ui-body-sm text-destructive"> <p role="alert" className="text-ui-body-sm text-destructive">
{error} {error}
</p> </p>
) : null} ) : null}
{state && fields.length === 0 && groups.length === 0 ? (
<p className="text-ui-body-sm text-muted-foreground">
<Trans>Nothing yet</Trans>
</p>
) : null}
{fields.length > 0 ? ( {fields.length > 0 ? (
<section className="rounded-xl border border-border px-4 py-4"> <ul className="space-y-3">
<ul className="space-y-3"> {fields.map(({ field, value }) => (
{fields.map(({ field, value }) => ( <Row
<Row key={field}
key={field} testId={`profile-field-${field}`}
testId={`profile-field-${field}`} value={
value={ field === "language"
field === "language" ? displayLanguageTag(value, uiLocale)
? displayLanguageTag(value, uiLocale) : field === "locale"
: field === "locale" ? displayRegionTag(value, uiLocale)
? displayRegionTag(value, uiLocale) : value
: value }
} detail={fieldLabels[field]}
detail={fieldLabels[field]} actions={(describedBy) =>
actions={(describedBy) => deleteButton(
deleteButton( describedBy,
describedBy, () => void apply(() => rpc.profile.update(CLEARED_PROFILE_FIELD[field])),
() => void apply(() => rpc.profile.update(CLEARED_PROFILE_FIELD[field])), )
) }
} />
/> ))}
))} </ul>
</ul>
</section>
) : null} ) : null}
{groups.map((group) => ( {groups.map((group) => (
<section <div key={group.category} data-testid={`user-facts-${group.category}`}>
key={group.category} <h4 className="text-ui-body-sm text-muted-foreground">
data-testid={`user-facts-${group.category}`}
className="rounded-xl border border-border px-4 py-4"
>
<h3 className="text-ui-body font-medium text-foreground">
{categoryLabels[group.category]} {categoryLabels[group.category]}
</h3> </h4>
<ul className="mt-3 space-y-3"> <ul className="mt-2 space-y-3">
{group.facts.map((fact) => {group.facts.map((fact) =>
fact.id === editingId ? ( fact.id === editingId ? (
<UserFactEditor <UserFactEditor
@ -269,7 +266,7 @@ export function UserProfileSettings() {
), ),
)} )}
</ul> </ul>
</section> </div>
))} ))}
{fields.length > 0 || groups.length > 0 ? ( {fields.length > 0 || groups.length > 0 ? (
<Button variant="destructive" disabled={busy} onClick={() => setForgetOpen(true)}> <Button variant="destructive" disabled={busy} onClick={() => setForgetOpen(true)}>
@ -300,6 +297,6 @@ export function UserProfileSettings() {
</AlertDialogFooter> </AlertDialogFooter>
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
</div> </section>
); );
} }

View File

@ -311,7 +311,7 @@ msgstr "Über Gruppen"
msgid "About spaces" msgid "About spaces"
msgstr "Über Spaces" msgstr "Über Spaces"
#: src/pages/SettingsOverlay.tsx #: src/components/UserProfileSettings.tsx
msgid "About you" msgid "About you"
msgstr "Über dich" msgstr "Über dich"
@ -3645,10 +3645,6 @@ msgstr "Nichts läuft mehr"
msgid "Nothing remembered yet" msgid "Nothing remembered yet"
msgstr "Noch nichts gemerkt" msgstr "Noch nichts gemerkt"
#: src/components/UserProfileSettings.tsx
msgid "Nothing yet"
msgstr "Noch nichts"
#: src/pages/SettingsOverlay.tsx #: src/pages/SettingsOverlay.tsx
#: src/pages/shell/bot-panel.tsx #: src/pages/shell/bot-panel.tsx
msgid "Notifications" msgid "Notifications"

View File

@ -311,7 +311,7 @@ msgstr "About groups"
msgid "About spaces" msgid "About spaces"
msgstr "About spaces" msgstr "About spaces"
#: src/pages/SettingsOverlay.tsx #: src/components/UserProfileSettings.tsx
msgid "About you" msgid "About you"
msgstr "About you" msgstr "About you"
@ -3645,10 +3645,6 @@ msgstr "Nothing is still running"
msgid "Nothing remembered yet" msgid "Nothing remembered yet"
msgstr "Nothing remembered yet" msgstr "Nothing remembered yet"
#: src/components/UserProfileSettings.tsx
msgid "Nothing yet"
msgstr "Nothing yet"
#: src/pages/SettingsOverlay.tsx #: src/pages/SettingsOverlay.tsx
#: src/pages/shell/bot-panel.tsx #: src/pages/shell/bot-panel.tsx
msgid "Notifications" msgid "Notifications"

View File

@ -311,7 +311,7 @@ msgstr ""
msgid "About spaces" msgid "About spaces"
msgstr "" msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/components/UserProfileSettings.tsx
msgid "About you" msgid "About you"
msgstr "" msgstr ""
@ -3645,10 +3645,6 @@ msgstr "No queda nada en ejecución"
msgid "Nothing remembered yet" msgid "Nothing remembered yet"
msgstr "Aún no hay recuerdos" msgstr "Aún no hay recuerdos"
#: src/components/UserProfileSettings.tsx
msgid "Nothing yet"
msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/pages/SettingsOverlay.tsx
#: src/pages/shell/bot-panel.tsx #: src/pages/shell/bot-panel.tsx
msgid "Notifications" msgid "Notifications"

View File

@ -311,7 +311,7 @@ msgstr ""
msgid "About spaces" msgid "About spaces"
msgstr "" msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/components/UserProfileSettings.tsx
msgid "About you" msgid "About you"
msgstr "" msgstr ""
@ -3645,10 +3645,6 @@ msgstr "कुछ भी नहीं चल रहा"
msgid "Nothing remembered yet" msgid "Nothing remembered yet"
msgstr "अभी तक कुछ याद नहीं किया गया" msgstr "अभी तक कुछ याद नहीं किया गया"
#: src/components/UserProfileSettings.tsx
msgid "Nothing yet"
msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/pages/SettingsOverlay.tsx
#: src/pages/shell/bot-panel.tsx #: src/pages/shell/bot-panel.tsx
msgid "Notifications" msgid "Notifications"

View File

@ -311,7 +311,7 @@ msgstr ""
msgid "About spaces" msgid "About spaces"
msgstr "" msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/components/UserProfileSettings.tsx
msgid "About you" msgid "About you"
msgstr "" msgstr ""
@ -3645,10 +3645,6 @@ msgstr "실행 중인 작업 없음"
msgid "Nothing remembered yet" msgid "Nothing remembered yet"
msgstr "아직 기억한 내용이 없습니다" msgstr "아직 기억한 내용이 없습니다"
#: src/components/UserProfileSettings.tsx
msgid "Nothing yet"
msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/pages/SettingsOverlay.tsx
#: src/pages/shell/bot-panel.tsx #: src/pages/shell/bot-panel.tsx
msgid "Notifications" msgid "Notifications"

View File

@ -311,7 +311,7 @@ msgstr ""
msgid "About spaces" msgid "About spaces"
msgstr "" msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/components/UserProfileSettings.tsx
msgid "About you" msgid "About you"
msgstr "" msgstr ""
@ -3645,10 +3645,6 @@ msgstr "Nada ainda está em execução"
msgid "Nothing remembered yet" msgid "Nothing remembered yet"
msgstr "Nada memorizado ainda" msgstr "Nada memorizado ainda"
#: src/components/UserProfileSettings.tsx
msgid "Nothing yet"
msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/pages/SettingsOverlay.tsx
#: src/pages/shell/bot-panel.tsx #: src/pages/shell/bot-panel.tsx
msgid "Notifications" msgid "Notifications"

View File

@ -311,7 +311,7 @@ msgstr "О группах"
msgid "About spaces" msgid "About spaces"
msgstr "О пространствах" msgstr "О пространствах"
#: src/pages/SettingsOverlay.tsx #: src/components/UserProfileSettings.tsx
msgid "About you" msgid "About you"
msgstr "" msgstr ""
@ -3645,10 +3645,6 @@ msgstr ""
msgid "Nothing remembered yet" msgid "Nothing remembered yet"
msgstr "Пока ничего не запомнено" msgstr "Пока ничего не запомнено"
#: src/components/UserProfileSettings.tsx
msgid "Nothing yet"
msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/pages/SettingsOverlay.tsx
#: src/pages/shell/bot-panel.tsx #: src/pages/shell/bot-panel.tsx
msgid "Notifications" msgid "Notifications"

View File

@ -311,7 +311,7 @@ msgstr ""
msgid "About spaces" msgid "About spaces"
msgstr "" msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/components/UserProfileSettings.tsx
msgid "About you" msgid "About you"
msgstr "" msgstr ""
@ -3645,10 +3645,6 @@ msgstr "Hâlâ çalışan bir şey yok"
msgid "Nothing remembered yet" msgid "Nothing remembered yet"
msgstr "Henüz hatırlanan bir şey yok" msgstr "Henüz hatırlanan bir şey yok"
#: src/components/UserProfileSettings.tsx
msgid "Nothing yet"
msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/pages/SettingsOverlay.tsx
#: src/pages/shell/bot-panel.tsx #: src/pages/shell/bot-panel.tsx
msgid "Notifications" msgid "Notifications"

View File

@ -311,7 +311,7 @@ msgstr ""
msgid "About spaces" msgid "About spaces"
msgstr "" msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/components/UserProfileSettings.tsx
msgid "About you" msgid "About you"
msgstr "" msgstr ""
@ -3645,10 +3645,6 @@ msgstr "没有仍在运行的任务"
msgid "Nothing remembered yet" msgid "Nothing remembered yet"
msgstr "暂无记忆" msgstr "暂无记忆"
#: src/components/UserProfileSettings.tsx
msgid "Nothing yet"
msgstr ""
#: src/pages/SettingsOverlay.tsx #: src/pages/SettingsOverlay.tsx
#: src/pages/shell/bot-panel.tsx #: src/pages/shell/bot-panel.tsx
msgid "Notifications" msgid "Notifications"

View File

@ -15,6 +15,7 @@ import { SuccessPop } from "../components/ai/primitives";
import { ComputersUnavailableHint } from "../components/ComputersUnavailableHint"; import { ComputersUnavailableHint } from "../components/ComputersUnavailableHint";
import { DesktopUpdateSection } from "../components/DesktopUpdates"; import { DesktopUpdateSection } from "../components/DesktopUpdates";
import { SoftwareUpdateSection } from "../components/SoftwareUpdateSection"; import { SoftwareUpdateSection } from "../components/SoftwareUpdateSection";
import { UserProfileSettings } from "../components/UserProfileSettings";
import { authClient } from "../lib/auth"; import { authClient } from "../lib/auth";
import { getActiveUiLocale, setUiLocale } from "../lib/i18n"; import { getActiveUiLocale, setUiLocale } from "../lib/i18n";
import { import {
@ -65,6 +66,8 @@ export function GeneralSettingsPanels({
{email ? <p className="mt-1 text-ui-body-sm text-muted-foreground/70">{email}</p> : null} {email ? <p className="mt-1 text-ui-body-sm text-muted-foreground/70">{email}</p> : null}
</section> </section>
<UserProfileSettings />
<ChangePasswordSection email={email} /> <ChangePasswordSection email={email} />
{messagingEnabled && onOpenMessaging ? ( {messagingEnabled && onOpenMessaging ? (

View File

@ -12,7 +12,6 @@ import {
Monitor, Monitor,
Settings, Settings,
ShieldCheck, ShieldCheck,
UserRound,
Volume2, Volume2,
XIcon, XIcon,
} from "lucide-react"; } from "lucide-react";
@ -20,7 +19,6 @@ import { type ComponentType, useEffect, useRef, useState } from "react";
import { computersAreUnavailable } from "../components/ComputersUnavailableHint"; import { computersAreUnavailable } from "../components/ComputersUnavailableHint";
import { MediaSettings } from "../components/MediaSettings"; import { MediaSettings } from "../components/MediaSettings";
import { NotificationSettingsPanel } from "../components/NotificationSettingsPanel"; import { NotificationSettingsPanel } from "../components/NotificationSettingsPanel";
import { UserProfileSettings } from "../components/UserProfileSettings";
import { import {
ComputerSettingsPanel, ComputerSettingsPanel,
GeneralSettingsPanels, GeneralSettingsPanels,
@ -38,7 +36,6 @@ export type SettingsSection =
| "integrations" | "integrations"
| "models" | "models"
| "memory" | "memory"
| "profile"
| "voice" | "voice"
| "media" | "media"
| "notifications" | "notifications"
@ -108,7 +105,6 @@ export function SettingsOverlay({
{ id: "integrations", label: t`Integrations`, icon: LayoutGrid }, { id: "integrations", label: t`Integrations`, icon: LayoutGrid },
{ id: "models", label: t`Models`, icon: Cpu }, { id: "models", label: t`Models`, icon: Cpu },
{ id: "memory", label: t`Memory`, icon: Brain }, { id: "memory", label: t`Memory`, icon: Brain },
{ id: "profile", label: t`About you`, icon: UserRound },
{ id: "voice", label: t`Voice`, icon: Volume2 }, { id: "voice", label: t`Voice`, icon: Volume2 },
...(isDeploymentOwner ? [{ id: "media" as const, label: t`Media`, icon: Clapperboard }] : []), ...(isDeploymentOwner ? [{ id: "media" as const, label: t`Media`, icon: Clapperboard }] : []),
{ id: "notifications", label: t`Notifications`, icon: Bell }, { id: "notifications", label: t`Notifications`, icon: Bell },
@ -247,7 +243,6 @@ export function SettingsOverlay({
onOpenMcp={onOpenMcp ? () => leaveSettings(onOpenMcp) : undefined} onOpenMcp={onOpenMcp ? () => leaveSettings(onOpenMcp) : undefined}
/> />
) : null} ) : null}
{section === "profile" ? <UserProfileSettings /> : null}
{section === "media" && isDeploymentOwner ? <MediaSettings /> : null} {section === "media" && isDeploymentOwner ? <MediaSettings /> : null}
{section === "notifications" ? <NotificationSettingsPanel /> : null} {section === "notifications" ? <NotificationSettingsPanel /> : null}
{section === "usage" ? ( {section === "usage" ? (