Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions desktop/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export default defineConfig({
"**/profile-nsec-reveal.spec.ts",
"**/profile-backup-settings.spec.ts",
"**/signout-confirmation.spec.ts",
"**/settings-section-layout.spec.ts",
"**/agent-provider-dropdowns.spec.ts",
"**/agent-lifecycle-feedback.spec.ts",
"**/agent-access-warning.spec.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { useUsersBatchQuery } from "@/features/profile/hooks";
import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar";
import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover";
import { SettingsOptionGroup } from "@/features/settings/ui/SettingsOptionGroup";
import { SettingsSectionHeader } from "@/features/settings/ui/SettingsSectionHeader";
import type {
RelayMember,
Expand Down Expand Up @@ -314,17 +315,16 @@ export function CommunityMembersSettingsCard({
description="Manage members and community access."
/>

<div className="overflow-hidden rounded-xl border border-border/70 bg-background/70 divide-y divide-border/55">
<div className="flex min-h-14 items-center px-4 py-3">
<h2 className="text-lg font-semibold tracking-tight">
<SettingsOptionGroup
title={
<>
Members
{members.length > 0 ? (
<span className="ml-1.5 text-sm font-normal text-muted-foreground">
{members.length}
</span>
<span className="ml-1.5 font-normal">{members.length}</span>
) : null}
</h2>
</div>
</>
}
>
<div className="space-y-3 p-4 sm:p-5">
<div className="relative">
<Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
Expand Down Expand Up @@ -376,7 +376,7 @@ export function CommunityMembersSettingsCard({
/>
)}
</div>
</div>
</SettingsOptionGroup>

<CommunityInviteDialog
isOwner={currentRole === "owner"}
Expand Down
9 changes: 0 additions & 9 deletions desktop/src/features/settings/UpdateChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export function UpdateChecker() {
{status.state === "idle" && (
<SettingsOptionRow>
<div className="min-w-0">
<p className="text-sm font-medium">Update status</p>
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
Expand All @@ -37,7 +36,6 @@ export function UpdateChecker() {
{status.state === "checking" && (
<SettingsOptionRow>
<div className="min-w-0">
<p className="text-sm font-medium">Update status</p>
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
Expand All @@ -51,7 +49,6 @@ export function UpdateChecker() {
{status.state === "up-to-date" && (
<SettingsOptionRow>
<div className="min-w-0">
<p className="text-sm font-medium">Update status</p>
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
Expand All @@ -68,7 +65,6 @@ export function UpdateChecker() {
{status.state === "unavailable" && (
<SettingsOptionRow>
<div className="min-w-0">
<p className="text-sm font-medium">Update status</p>
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
Expand Down Expand Up @@ -107,7 +103,6 @@ export function UpdateChecker() {
{status.state === "available" && (
<SettingsOptionRow>
<div className="min-w-0">
<p className="text-sm font-medium">Update status</p>
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
Expand All @@ -121,7 +116,6 @@ export function UpdateChecker() {
{status.state === "downloading" && (
<SettingsOptionRow>
<div className="min-w-0">
<p className="text-sm font-medium">Update status</p>
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
Expand All @@ -135,7 +129,6 @@ export function UpdateChecker() {
{status.state === "installing" && (
<SettingsOptionRow>
<div className="min-w-0">
<p className="text-sm font-medium">Update status</p>
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
Expand All @@ -149,7 +142,6 @@ export function UpdateChecker() {
{status.state === "ready" && (
<SettingsOptionRow>
<div className="min-w-0">
<p className="text-sm font-medium">Update status</p>
<p
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
Expand All @@ -166,7 +158,6 @@ export function UpdateChecker() {
{status.state === "error" && (
<SettingsOptionRow>
<div className="min-w-0">
<p className="text-sm font-medium">Update status</p>
<p className="text-sm font-normal text-destructive">
Update failed: {status.message}
</p>
Expand Down
5 changes: 3 additions & 2 deletions desktop/src/features/settings/ui/AgentsSettingsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AgentDefaultsSettingsCard } from "./AgentDefaultsSettingsCard";
import { HarnessesSettingsPanel } from "./HarnessesSettingsPanel";
import { PreventSleepSettingsCard } from "./PreventSleepSettingsCard";
import { SettingsOptionGroupList } from "./SettingsOptionGroup";
import { SettingsSectionHeader } from "./SettingsSectionHeader";

export function AgentsSettingsPanel() {
Expand All @@ -11,11 +12,11 @@ export function AgentsSettingsPanel() {
description="Control how agents behave in conversations and run on this machine."
/>

<div className="flex flex-col gap-4">
<SettingsOptionGroupList>
<PreventSleepSettingsCard />
<HarnessesSettingsPanel />
<AgentDefaultsSettingsCard />
</div>
</SettingsOptionGroupList>
</section>
);
}
10 changes: 7 additions & 3 deletions desktop/src/features/settings/ui/KeyboardShortcutsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import {
getPlatformKeys,
type KeyboardShortcut,
} from "@/shared/lib/keyboard-shortcuts";
import { SettingsOptionGroup, SettingsOptionRow } from "./SettingsOptionGroup";
import {
SettingsOptionGroup,
SettingsOptionGroupList,
SettingsOptionRow,
} from "./SettingsOptionGroup";
import { SettingsSectionHeader } from "./SettingsSectionHeader";

function KeyCombo({ shortcut }: { shortcut: KeyboardShortcut }) {
Expand Down Expand Up @@ -38,7 +42,7 @@ export function KeyboardShortcutsCard() {
description="All available keyboard shortcuts. Shortcuts are read-only."
/>

<div className="space-y-4">
<SettingsOptionGroupList>
{[...categories.entries()].map(([category, shortcuts]) => (
<SettingsOptionGroup key={category} title={category}>
{shortcuts.map((shortcut) => (
Expand All @@ -62,7 +66,7 @@ export function KeyboardShortcutsCard() {
))}
</SettingsOptionGroup>
))}
</div>
</SettingsOptionGroupList>
</section>
);
}
14 changes: 9 additions & 5 deletions desktop/src/features/settings/ui/NotificationSettingsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import {
import { cn } from "@/shared/lib/cn";
import { Button } from "@/shared/ui/button";
import { Switch } from "@/shared/ui/switch";
import { SettingsOptionGroup, SettingsOptionRow } from "./SettingsOptionGroup";
import {
SettingsOptionGroup,
SettingsOptionGroupList,
SettingsOptionRow,
} from "./SettingsOptionGroup";
import { SettingsSectionHeader } from "./SettingsSectionHeader";
import { SoundPicker } from "./SoundPicker";

Expand Down Expand Up @@ -76,7 +80,7 @@ export function NotificationSettingsCard({
: "Off"}
</span>

<div className="flex flex-col gap-4">
<SettingsOptionGroupList>
<SettingsOptionGroup title="Desktop">
<SettingsOptionRow>
<div className="min-w-0">
Expand Down Expand Up @@ -169,7 +173,7 @@ export function NotificationSettingsCard({
</SettingsOptionGroup>

{anyAlertsOn ? (
<>
<div className="space-y-4">
<SettingsOptionGroup title="Alert sounds">
{visibleSlots.map((slot) => {
const comingSoon = COMING_SOON_SLOTS.has(slot);
Expand Down Expand Up @@ -249,7 +253,7 @@ export function NotificationSettingsCard({
)}
</Button>
</div>
</>
</div>
) : null}
</>
) : null}
Expand Down Expand Up @@ -281,7 +285,7 @@ export function NotificationSettingsCard({
/>
</SettingsOptionRow>
</SettingsOptionGroup>
</div>
</SettingsOptionGroupList>

{permissionBlocked && (
<p className="mt-4 rounded-xl border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
Expand Down
44 changes: 22 additions & 22 deletions desktop/src/features/settings/ui/ProfileSettingsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import { Input } from "@/shared/ui/input";
import { Spinner } from "@/shared/ui/spinner";
import { Textarea } from "@/shared/ui/textarea";
import { PrivateKeyBackupRow } from "./PrivateKeyBackupRow";
import {
SettingsOptionGroup,
SettingsOptionGroupList,
} from "./SettingsOptionGroup";
import { SettingsSectionHeader } from "./SettingsSectionHeader";
import { SignOutSection } from "./SignOutSection";
import { writeTextToClipboard } from "@/shared/lib/clipboard";
Expand Down Expand Up @@ -668,24 +672,20 @@ export function ProfileSettingsCard({
data-testid="profile-readonly-content"
inert={isAvatarEditorOpen ? true : undefined}
>
<div className="space-y-12">
<div
className="overflow-hidden rounded-xl border border-border/70 bg-background/70 shadow-xs divide-y divide-border/55"
data-testid="profile-metadata-card"
>
<div className="flex min-h-14 items-center justify-between gap-4 px-4 py-3">
<h2 className="text-lg font-semibold tracking-tight">
Profile info
</h2>
<SettingsOptionGroupList>
<SettingsOptionGroup
headerAction={
<EditProfileMetadataButton
disabled={updateProfileMutation.isPending}
isEditing={isEditingProfileMetadata}
label="profile info"
onClick={handleProfileMetadataEdit}
testId="profile-metadata-edit"
/>
</div>

}
data-testid="profile-metadata-card"
title="Profile info"
>
<div className="flex min-h-16 items-center gap-4 px-4 py-3">
<div className="min-w-0 flex-1 space-y-1">
<label
Expand Down Expand Up @@ -756,23 +756,23 @@ export function ProfileSettingsCard({
)}
</div>
</div>
</div>
</SettingsOptionGroup>

<div>
<SettingsOptionGroup title="Identity">
<details
className="group overflow-hidden rounded-xl border border-border/70 bg-background/70 shadow-xs"
className="group divide-y divide-border/55"
data-testid="profile-identity-card"
>
<summary
className="group/identity flex cursor-pointer list-none items-center justify-between gap-4 px-4 py-3 text-sm transition-colors duration-150 ease-out hover:bg-muted/40 focus-visible:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring [&::-webkit-details-marker]:hidden"
className="group/identity flex min-h-16 cursor-pointer list-none items-center justify-between gap-4 px-4 py-3 transition-colors duration-150 ease-out hover:bg-muted/40 focus-visible:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring [&::-webkit-details-marker]:hidden"
data-testid="profile-identity-toggle"
>
<div className="min-w-0">
<h2 className="text-lg font-semibold tracking-tight">
Identity
</h2>
<p className="text-sm font-medium">
Identity details
</p>
<p
className="mt-1 text-sm font-normal text-muted-foreground/70"
className="text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
>
Your keypair and NIP-05 handle are fixed for
Expand All @@ -782,7 +782,7 @@ export function ProfileSettingsCard({
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-[color,transform] duration-150 ease-out group-open:rotate-180 group-hover/identity:text-foreground group-focus-visible/identity:text-foreground" />
</summary>
<div
className="border-t border-border/55 divide-y divide-border/55"
className="divide-y divide-border/55"
data-testid="profile-identity-details"
>
<IdentityRow
Expand All @@ -802,8 +802,8 @@ export function ProfileSettingsCard({
<PrivateKeyBackupRow />
</div>
</details>
</div>
</div>
</SettingsOptionGroup>
</SettingsOptionGroupList>
</div>

{shouldRenderAvatarEditor ? (
Expand Down
53 changes: 38 additions & 15 deletions desktop/src/features/settings/ui/SettingsOptionGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,24 @@ export function SettingsOptionGroup({
surface?: "framed" | "soft";
title?: React.ReactNode;
}) {
const hasHeader = Boolean(title || description || headerAction);

return (
<div
className={cn(
surface === "soft"
? "overflow-hidden rounded-2xl bg-muted/20"
: "overflow-hidden rounded-xl border border-border/70 bg-background/70 divide-y divide-border/55",
className,
)}
{...props}
>
{title ? (
<div className="flex min-h-14 items-center justify-between gap-4 px-4 py-3">
<div className="min-w-0">
<h2 className="text-lg font-semibold tracking-tight">{title}</h2>
<div className={cn("space-y-2", className)} {...props}>
Comment thread
klopez4212 marked this conversation as resolved.
Comment thread
klopez4212 marked this conversation as resolved.
{hasHeader ? (
<div
className="flex min-h-7 items-end gap-3 px-4"
data-slot="settings-section-header"
>
<div className="min-w-0 flex-1">
{title ? (
<h2 className="text-sm font-semibold text-muted-foreground/70">
{title}
</h2>
) : null}
{description ? (
<p
className="mt-1 text-sm font-normal text-muted-foreground/70"
className="mt-0.5 text-sm font-normal text-muted-foreground/70"
data-settings-subcopy
>
{description}
Expand All @@ -42,11 +43,33 @@ export function SettingsOptionGroup({
{headerAction ? <div className="shrink-0">{headerAction}</div> : null}
</div>
) : null}
{children}
<div
className={cn(
surface === "soft"
? "overflow-hidden rounded-2xl bg-muted/20"
: "divide-y divide-border/55 overflow-hidden rounded-xl border border-border/70 bg-background/70",
)}
data-slot="settings-section-card"
>
{children}
</div>
</div>
);
}

export function SettingsOptionGroupList({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("space-y-12", className)}
data-slot="settings-section-list"
{...props}
/>
);
}

export function SettingsOptionRow({
className,
...props
Expand Down
Loading
Loading