fix(ui): granular sleep sources, viewport-safe tour, one aligned settings language

A night with coarse apple-health samples next to granular watch data
showed only awake and asleep — the per-session source pick now prefers
the source that knows its stages. Tour popovers clamp into the
viewport, wrap their footer and fall back to a bottom sheet on narrow
screens. Settings and admin sections share one alignment rule — icon
column left, everything else flush on the content column — with neutral
header icons, no duplicated section titles under the shell heading, and
every select on the account-style idiom with a visible chevron. The
dashboard's customize icon matches insights, the target-settings link
leaves the home charts, and the mood heatmap adopts the damped fills
the other mood charts already use.
This commit is contained in:
Marc-André Bombeck committed 2026-06-10 21:19:05 +02:00
1 parent 9f2c403f84
commit 024da9fe96
67 files changed
+545 -344

No files matched your search

+4 -2
View File
@@ -33,7 +33,9 @@ import type { LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
export interface SettingsCardHeaderProps {
/** Lucide icon component; rendered as `text-primary h-5 w-5`. */
/** Lucide icon component; rendered as `text-muted-foreground h-5 w-5`.
* Neutral by design — primary/purple stays reserved for actions and
* highlight states, never for section-header iconography. */
icon: LucideIcon;
/** Card title — rendered as `<h2 class="text-lg font-semibold">`.
* Accepts a node so a card can wrap the title in a link. */
@@ -68,7 +70,7 @@ export function SettingsCardHeader({
return (
<header className={cn("flex items-start gap-2", className)}>
<Icon
className="text-primary mt-0.5 h-5 w-5 shrink-0"
className="text-muted-foreground mt-0.5 h-5 w-5 shrink-0"
aria-hidden="true"
/>
{/* Title + description share one column to the RIGHT of the icon, so the
+4 -4
View File
@@ -229,7 +229,7 @@ export function AboutMeSection({
</div>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<div className="grid gap-4 pl-7 sm:grid-cols-2">
{structuredField(
"conditions",
t("settings.ai.aboutMe.conditionsLabel"),
@@ -249,7 +249,7 @@ export function AboutMeSection({
</div>
</div>
<div className="space-y-1.5">
<div className="space-y-1.5 pl-7">
<Label htmlFor="settings-about-me-freetext">
{t("settings.ai.aboutMe.freeTextLabel")}
</Label>
@@ -267,7 +267,7 @@ export function AboutMeSection({
/>
</div>
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="flex flex-wrap items-center justify-between gap-3 pl-7">
<p
data-testid="settings-about-me-count"
className="text-muted-foreground text-xs tabular-nums"
@@ -354,7 +354,7 @@ export function AboutMeSection({
</p>
)}
<p className="text-muted-foreground border-border border-t pt-3 text-xs">
<p className="text-muted-foreground border-border border-t pt-3 pl-7 text-xs">
{t("settings.ai.aboutMe.hint")} {t("settings.ai.aboutMe.profileHint")}
</p>
</section>
+11 -11
View File
@@ -238,7 +238,7 @@ export function AboutSection() {
<header className="space-y-1">
<h1
id="settings-section-about-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.about.title")}
</h1>
@@ -255,14 +255,14 @@ export function AboutSection() {
<SettingsCardHeader icon={Info} title="HealthLog" className="mb-4" />
{isLoading || !version ? (
<div className="flex items-center gap-2">
<div className="flex items-center gap-2 pl-7">
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin motion-reduce:animate-none" />
<span className="text-muted-foreground text-sm">
{t("common.loading")}
</span>
</div>
) : (
<dl className="flex flex-wrap items-baseline gap-x-6 gap-y-3 text-sm">
<dl className="flex flex-wrap items-baseline gap-x-6 gap-y-3 pl-7 text-sm">
<div className="flex items-baseline gap-2">
<dt className="text-muted-foreground text-xs tracking-wide uppercase">
{t("settings.about.version")}
@@ -327,7 +327,7 @@ export function AboutSection() {
title={t("settings.about.linksHeading")}
className="mb-4"
/>
<div className="flex flex-col gap-2 sm:flex-row sm:flex-wrap">
<div className="flex flex-col gap-2 pl-7 sm:flex-row sm:flex-wrap">
<Button asChild variant="outline" size="sm">
<a
href={version.repository}
@@ -372,16 +372,16 @@ export function AboutSection() {
tour" card so the two surfaces feel consistent. */}
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between sm:gap-2">
<div className="space-y-1">
<div className="flex items-center gap-2">
<Compass className="text-primary h-5 w-5" />
<div className="flex items-start gap-2">
<Compass className="text-muted-foreground mt-0.5 h-5 w-5 shrink-0" />
<div className="space-y-1">
<h2 className="text-lg font-semibold">
{t("settings.about.tourReplay")}
</h2>
<p className="text-muted-foreground text-xs">
{t("settings.about.tourReplayHint")}
</p>
</div>
<p className="text-muted-foreground text-xs">
{t("settings.about.tourReplayHint")}
</p>
</div>
<Button
type="button"
@@ -402,7 +402,7 @@ export function AboutSection() {
{tourFeedback && (
<p
role="alert"
className={`mt-2 text-xs ${
className={`mt-2 pl-7 text-xs ${
tourFeedback.type === "success"
? "text-success"
: "text-destructive"
+20 -18
View File
@@ -383,7 +383,7 @@ export function AccountSection() {
<header className="space-y-1">
<h1
id="settings-section-account-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.account.title")}
</h1>
@@ -402,7 +402,7 @@ export function AccountSection() {
title={t("settings.profile")}
className="mb-4"
/>
<form onSubmit={handleSaveProfile} className="space-y-4">
<form onSubmit={handleSaveProfile} className="space-y-4 pl-7">
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor="username">{t("settings.username")}</Label>
@@ -615,8 +615,10 @@ export function AccountSection() {
title={t("settings.passkeys")}
className="mb-4"
/>
<PasskeyListSection isAuthenticated={isAuthenticated} />
<div className="mt-4 flex justify-end">
<div className="pl-7">
<PasskeyListSection isAuthenticated={isAuthenticated} />
</div>
<div className="mt-4 flex justify-end pl-7">
<Button
variant="outline"
onClick={handleAddPasskey}
@@ -653,16 +655,16 @@ export function AccountSection() {
right border by ~48 px). */}
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between sm:gap-2">
<div className="space-y-1">
<div className="flex items-center gap-2">
<Shield className="text-primary h-5 w-5" />
<div className="flex items-start gap-2">
<Shield className="text-muted-foreground mt-0.5 h-5 w-5 shrink-0" />
<div className="space-y-1">
<h2 className="text-lg font-semibold">
{t("settings.passwordReset")}
</h2>
<p className="text-muted-foreground text-xs">
{t("settings.changePasswordDescription")}
</p>
</div>
<p className="text-muted-foreground text-xs">
{t("settings.changePasswordDescription")}
</p>
</div>
<Button
type="button"
@@ -683,16 +685,16 @@ export function AccountSection() {
as the password card so both action surfaces look identical. */}
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between sm:gap-2">
<div className="space-y-1">
<div className="flex items-center gap-2">
<Compass className="text-primary h-5 w-5" />
<div className="flex items-start gap-2">
<Compass className="text-muted-foreground mt-0.5 h-5 w-5 shrink-0" />
<div className="space-y-1">
<h2 className="text-lg font-semibold">
{t("onboarding.tour.restart")}
</h2>
<p className="text-muted-foreground text-xs">
{t("onboarding.tour.restartHint")}
</p>
</div>
<p className="text-muted-foreground text-xs">
{t("onboarding.tour.restartHint")}
</p>
</div>
<Button
type="button"
@@ -1156,10 +1158,10 @@ function AvatarSection() {
title={t("settings.avatar.title")}
className="mb-4"
/>
<p className="text-muted-foreground mb-4 text-sm">
<p className="text-muted-foreground mb-4 pl-7 text-sm">
{t("settings.avatar.description")}
</p>
<div className="flex items-center gap-4">
<div className="flex items-center gap-4 pl-7">
<Avatar className="size-16">
{avatarUrl && <AvatarImage src={avatarUrl} alt={user.username} />}
<AvatarFallback className="text-base">
+3 -3
View File
@@ -57,7 +57,7 @@ export function AdvancedSection() {
<header className="space-y-1">
<h1
id="settings-section-advanced-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.advanced.title")}
</h1>
@@ -154,12 +154,12 @@ function ResearchModeCard() {
data-slot="settings-research-mode-card"
>
<div className="flex items-center gap-2">
<BookOpenCheck className="text-primary h-5 w-5" />
<BookOpenCheck className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("settings.researchMode.sectionTitle")}
</h2>
</div>
<p className="text-muted-foreground mt-1 text-xs">
<p className="text-muted-foreground mt-1 pl-7 text-xs">
{t("settings.researchMode.subtitle")}
</p>
+65 -50
View File
@@ -35,7 +35,9 @@ import { NativeSelect } from "@/components/ui/native-select";
import { Switch } from "@/components/ui/switch";
import { PasswordInput } from "@/components/ui/password-input";
import { AboutMeSection } from "@/components/settings/about-me-section";
import { SettingsCardHeader } from "@/components/settings/_card-header";
import { CoachMemorySection } from "@/components/settings/coach-memory-section";
import { CoachPrefsSection } from "@/components/settings/coach-prefs-section";
import { useAuth } from "@/hooks/use-auth";
import { formatDateTime } from "@/lib/format";
import { useTranslations } from "@/lib/i18n/context";
@@ -183,7 +185,7 @@ export function AiSection() {
<header className="space-y-1">
<h1
id="settings-section-ai-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.ai.title")}
</h1>
@@ -199,6 +201,12 @@ export function AiSection() {
through provider configuration before finding the toggle. */}
<DisableCoachCard isAuthenticated={isAuthenticated} />
{/* v1.16.1 — Coach preferences (tone, verbosity, data scope)
moved here from the in-chat sheet; the chat header gear
deep-links to this section. Gated on the Coach being
enabled. */}
{coachEnabled && <CoachPrefsSection isAuthenticated={isAuthenticated} />}
{/* v1.11.2 — "What the Coach remembers": durable-fact review +
forget controls. Gated on the Coach being enabled. */}
{coachEnabled && <CoachMemorySection isAuthenticated={isAuthenticated} />}
@@ -447,57 +455,64 @@ function AiInsightsCard({ isAuthenticated }: { isAuthenticated: boolean }) {
return (
<div className="bg-card border-border space-y-4 rounded-xl border p-6">
{/* v1.4.33 IW7 — the dedicated H2 ("KI-Insights") used to repeat
the parent section title ("KI-Auswertungen"). Both are gone now:
the section renders "Auswertungen" once at the top, and this
card carries only the Sparkles icon + the live provider-status
badges, mirroring the description that lives on the section
header. The configuration description is moved one level up
into the section subtitle so the visual hierarchy stays
two-level (section -> form) instead of three-level
(section -> card title -> form). */}
<div className="flex flex-wrap items-center justify-between gap-2">
<Sparkles className="text-primary h-5 w-5" aria-hidden="true" />
<ProviderStatusBadges
settings={insightsSettings}
activeProvider={chainData?.activeProvider ?? null}
{/* The card used to render an icon-only header row (Sparkles +
status badges, no title), which left the tile unanchored next
to its titled siblings. It now follows the shared
`SettingsCardHeader` contract: icon column, title + description
in the content column, status badges top-right — and the body
below indents to the title column (`pl-7`). */}
<SettingsCardHeader
icon={Sparkles}
title={t("settings.ai.providerCardTitle")}
description={t("settings.kiInsightsDescription")}
status={
<ProviderStatusBadges
settings={insightsSettings}
activeProvider={chainData?.activeProvider ?? null}
/>
}
/>
<div className="space-y-4 pl-7">
<ActiveProviderSelect
value={selectedProvider}
onChange={pickProvider}
/>
<ProviderConfigCard
provider={selectedProvider}
insightsSettings={insightsSettings}
userProvider={userProvider}
/>
<FallbackChainCard
chain={chainData?.configuredChain ?? []}
selected={selectedProvider}
onSelect={pickProvider}
/>
<RuntimeActionsRow
provider={selectedProvider}
userProvider={userProvider}
canRegenerate={
insightsSettings?.codexStatus === "connected" ||
insightsSettings?.hasAdminKey ||
Boolean(userProvider?.provider)
}
privacyMode={insightsSettings?.privacyMode ?? "aggregated"}
lastInsightAt={insightsSettings?.lastInsightAt ?? null}
onRegenerated={() =>
queryClient.invalidateQueries({
queryKey: queryKeys.insightsRoot(),
})
}
onPrivacyChanged={() =>
queryClient.invalidateQueries({
queryKey: queryKeys.insightsRoot(),
})
}
/>
</div>
<p className="text-muted-foreground text-xs">
{t("settings.kiInsightsDescription")}
</p>
<ActiveProviderSelect value={selectedProvider} onChange={pickProvider} />
<ProviderConfigCard
provider={selectedProvider}
insightsSettings={insightsSettings}
userProvider={userProvider}
/>
<FallbackChainCard
chain={chainData?.configuredChain ?? []}
selected={selectedProvider}
onSelect={pickProvider}
/>
<RuntimeActionsRow
provider={selectedProvider}
userProvider={userProvider}
canRegenerate={
insightsSettings?.codexStatus === "connected" ||
insightsSettings?.hasAdminKey ||
Boolean(userProvider?.provider)
}
privacyMode={insightsSettings?.privacyMode ?? "aggregated"}
lastInsightAt={insightsSettings?.lastInsightAt ?? null}
onRegenerated={() =>
queryClient.invalidateQueries({ queryKey: queryKeys.insightsRoot() })
}
onPrivacyChanged={() =>
queryClient.invalidateQueries({ queryKey: queryKeys.insightsRoot() })
}
/>
</div>
);
}
+2 -2
View File
@@ -43,7 +43,7 @@ export function ApiSection() {
<header className="space-y-1">
<h1
id="settings-section-api-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.api.title")}
</h1>
@@ -262,7 +262,7 @@ function ApiTokensCard() {
}
/>
<div className="mt-4 space-y-4">
<div className="mt-4 space-y-4 pl-7">
{/* The button mirrors the Input's responsive height (h-11 / sm:h-10)
so the row reads as one aligned control group instead of a tall
input next to a shorter `sm` button. */}
+2 -2
View File
@@ -139,8 +139,8 @@ export function CoachNudgeCard({
aria-live="polite"
className={
msgType === "error"
? "text-destructive mt-3 text-sm"
: "text-muted-foreground mt-3 text-sm"
? "text-destructive mt-3 pl-7 text-sm"
: "text-muted-foreground mt-3 pl-7 text-sm"
}
>
{msg}
@@ -72,7 +72,7 @@ export function CycleTrackingCard({
title={t("settings.cycleTracking.title")}
description={t("settings.cycleTracking.description")}
/>
<div className="mt-4 space-y-4">
<div className="mt-4 space-y-4 pl-7">
<div className="flex items-start justify-between gap-4">
<Label htmlFor="cycle-tracking-enable" className="text-sm">
{t("settings.cycleTracking.enable")}
@@ -312,7 +312,7 @@ export function DashboardLayoutSection({ id }: { id: string }) {
zurücksetzen" copy clipping the card edge at narrow widths. */}
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3">
<div className="flex items-center gap-2">
<LayoutDashboard className="text-primary h-5 w-5" />
<LayoutDashboard className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("dashboard.customizeTitle")}
</h2>
@@ -14,7 +14,7 @@ export function DashboardSection() {
<header className="space-y-1">
<h1
id="settings-section-dashboard-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.dashboard.title")}
</h1>
+2 -2
View File
@@ -80,7 +80,7 @@ export function ExportSection() {
<header className="space-y-1">
<h1
id="settings-section-export-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.export.title")}
</h1>
@@ -161,7 +161,7 @@ function ExportCardShell({
>
<div className="flex items-start justify-between gap-3">
<div className="flex items-center gap-2">
<Icon className="text-primary h-5 w-5 shrink-0" />
<Icon className="text-muted-foreground h-5 w-5 shrink-0" />
<h2 className="text-base font-semibold">{title}</h2>
</div>
<span className="border-border text-muted-foreground rounded-full border px-2 py-0.5 text-[11px] font-medium tracking-wide uppercase">
@@ -200,7 +200,7 @@ export function HealthRecordExportPanel() {
>
<div className="mb-1 flex items-center gap-2">
<FileText
className="text-primary h-5 w-5 shrink-0"
className="text-muted-foreground h-5 w-5 shrink-0"
aria-hidden="true"
/>
<h2
+1 -1
View File
@@ -95,7 +95,7 @@ function ImportCardShell({
className="bg-card border-border flex h-full flex-col rounded-xl border p-6"
>
<div className="flex items-center gap-2">
<Icon className="text-primary h-5 w-5 shrink-0" aria-hidden="true" />
<Icon className="text-muted-foreground h-5 w-5 shrink-0" aria-hidden="true" />
<h3 className="text-base font-semibold">{title}</h3>
</div>
<p className="text-muted-foreground mt-1 text-xs">{description}</p>
@@ -74,7 +74,7 @@ export function InjectionSitesCard({
title={t("settings.globalExcludedInjectionSitesLabel")}
description={t("settings.globalExcludedInjectionSitesHint")}
/>
<div className="mt-3 grid grid-cols-2 gap-2">
<div className="mt-3 grid grid-cols-2 gap-2 pl-7">
{INJECTION_SITE_KEYS.map((site) => (
<label key={site} className="flex items-center gap-2 text-sm">
<Checkbox
+1 -1
View File
@@ -26,7 +26,7 @@ export function InsightsSection() {
<header className="space-y-1">
<h1
id="settings-section-insights-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.insights.title")}
</h1>
@@ -257,7 +257,7 @@ export function IntegrationsSection() {
<header className="space-y-1">
<h1
id="settings-section-integrations-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.integrations.title")}
</h1>
@@ -463,7 +463,7 @@ function WithingsCard({
className="border-border/60 mt-4"
/>
<div className="mt-4 space-y-4">
<div className="mt-4 space-y-4 pl-7">
{errorMessage && <IntegrationErrorMessage message={errorMessage} />}
{/* v1.4.25 W5d — Withings activity-scope reconnect banner.
Only paints when (a) the user has an active Withings
@@ -920,7 +920,7 @@ function WhoopCard({
className="border-border/60 mt-4"
/>
<div className="mt-4 space-y-4">
<div className="mt-4 space-y-4 pl-7">
{errorMessage && <IntegrationErrorMessage message={errorMessage} />}
{/* Parked-integration resume CTA. Surfaces only when the row
state is `parked` (>24h of persistent failures). The button
@@ -1370,7 +1370,7 @@ function FitbitCard({
className="border-border/60 mt-4"
/>
<div className="mt-4 space-y-4">
<div className="mt-4 space-y-4 pl-7">
{errorMessage && <IntegrationErrorMessage message={errorMessage} />}
{/* Parked-integration resume CTA. Surfaces only when the row
state is `parked` (>24h of persistent failures). The button
@@ -4,6 +4,7 @@ import { useEffect, useRef, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { SmilePlus } from "lucide-react";
import { NativeSelect } from "@/components/ui/native-select";
import { Switch } from "@/components/ui/switch";
import { SettingsCardHeader } from "@/components/settings/_card-header";
import { useTranslations } from "@/lib/i18n/context";
@@ -181,16 +182,16 @@ export function MoodReminderCard({
}
/>
{enabled && (
<div className="mt-4 flex min-h-11 items-center gap-3">
<div className="mt-4 flex min-h-11 items-center gap-3 pl-7">
<label
htmlFor="mood-reminder-hour"
className="text-sm font-medium"
>
{t("notifications.moodReminder.hourLabel")}
</label>
<select
<NativeSelect
id="mood-reminder-hour"
className="border-input bg-background h-11 rounded-md border px-3 text-sm"
className="w-auto"
value={reminderHour}
disabled={!isAuthenticated || saving}
onChange={(e) => handleHourChange(Number(e.target.value))}
@@ -201,7 +202,7 @@ export function MoodReminderCard({
{`${h.toString().padStart(2, "0")}:00`}
</option>
))}
</select>
</NativeSelect>
</div>
)}
{msg && (
@@ -210,8 +211,8 @@ export function MoodReminderCard({
aria-live="polite"
className={
msgType === "error"
? "text-destructive mt-3 text-sm"
: "text-muted-foreground mt-3 text-sm"
? "text-destructive mt-3 pl-7 text-sm"
: "text-muted-foreground mt-3 pl-7 text-sm"
}
>
{msg}
@@ -136,12 +136,12 @@ export function NotificationStatusCard() {
data-testid="notification-status-empty"
>
<div className="flex items-center gap-2">
<Bell className="text-primary h-5 w-5" />
<Bell className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("settings.notificationStatus.title")}
</h2>
</div>
<p className="text-muted-foreground mt-1 text-sm">
<p className="text-muted-foreground mt-1 pl-7 text-sm">
{t("settings.notificationStatus.emptyDescription")}
</p>
</div>
@@ -152,12 +152,12 @@ export function NotificationStatusCard() {
<div className="bg-card border-border rounded-xl border p-6">
<header className="mb-4">
<div className="flex items-center gap-2">
<Bell className="text-primary h-5 w-5" />
<Bell className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("settings.notificationStatus.title")}
</h2>
</div>
<p className="text-muted-foreground text-xs">
<p className="text-muted-foreground pl-7 text-xs">
{t("settings.notificationStatus.description")}
</p>
</header>
@@ -77,7 +77,7 @@ export function NotificationsSection() {
<header className="space-y-1">
<h1
id="settings-section-notifications-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.notifications.title")}
</h1>
+1 -1
View File
@@ -91,7 +91,7 @@ export function NtfyCard({ isAuthenticated }: { isAuthenticated: boolean }) {
description={t("settings.ntfyDescription")}
/>
<div className="mt-4 space-y-4">
<div className="mt-4 space-y-4 pl-7">
<div className="flex items-center justify-between">
<Label htmlFor="ntfy-toggle">{t("settings.ntfyEnable")}</Label>
<Switch
@@ -72,7 +72,7 @@ export function SectionPlaceholder({ slug }: SectionPlaceholderProps) {
<header className="space-y-1">
<h1
id={`settings-section-${slug}-title`}
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{sectionTitle}
</h1>
+3 -3
View File
@@ -301,7 +301,7 @@ export function SourcesSection() {
<header className="space-y-1">
<h1
id="settings-section-sources-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.sources.title")}
</h1>
@@ -325,7 +325,7 @@ export function SourcesSection() {
<div className="bg-card border-border space-y-4 rounded-xl border p-6">
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3">
<div className="flex items-center gap-2">
<Layers className="text-primary h-5 w-5" />
<Layers className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("settings.sections.sources.cardTitle")}
</h2>
@@ -342,7 +342,7 @@ export function SourcesSection() {
</Button>
</div>
<p className="text-muted-foreground text-xs">
<p className="text-muted-foreground pl-7 text-xs">
{t("settings.sections.sources.help")}
</p>
+1 -1
View File
@@ -96,7 +96,7 @@ export function TelegramCard({
description={t("settings.telegramDescription")}
/>
<div className="mt-4 space-y-4">
<div className="mt-4 space-y-4 pl-7">
<form onSubmit={handleSave} className="space-y-4">
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
@@ -128,7 +128,7 @@ export function ThresholdsEditorSection({ id }: { id: string }) {
control affordance. */}
<div className="flex items-center justify-between">
<SlidersHorizontal
className="text-primary h-5 w-5"
className="text-muted-foreground h-5 w-5"
aria-hidden="true"
/>
{data && Object.keys(data.overrides).length > 0 && (
@@ -41,7 +41,7 @@ export function ThresholdsSection() {
<header className="space-y-1">
<h1
id="settings-section-thresholds-title"
className="text-2xl font-semibold tracking-tight"
className="sr-only"
>
{t("settings.sections.thresholds.title")}
</h1>
+1 -1
View File
@@ -208,7 +208,7 @@ export function WebPushCard() {
/>
{(!loading && !isSupported) || (!loading && isDenied) || msg ? (
<div className="mt-4 space-y-4">
<div className="mt-4 space-y-4 pl-7">
{!loading && !isSupported ? (
<p className="text-muted-foreground text-sm">
{t("settings.webPushNotSupported")}