diff --git a/src/frontend/src/components/Skeleton.tsx b/src/frontend/src/components/Skeleton.tsx
index b110f98..d1cad8a 100644
--- a/src/frontend/src/components/Skeleton.tsx
+++ b/src/frontend/src/components/Skeleton.tsx
@@ -174,6 +174,65 @@ export function SkeletonSystem() {
);
}
+/** Skeleton for the settings page. */
+export function SkeletonSettings() {
+ return (
+
+
+
+ {/* General section */}
+
+
+
+
+
+
+ {Array.from({ length: 4 }).map((_, i) => (
+
+
+
+
+ ))}
+
+
+
+ {/* Platform Settings section */}
+
+
+ {/* Format Profiles section */}
+
+
+ {/* Notifications section */}
+
+
+ );
+}
+
/** Skeleton for the channels list page. */
export function SkeletonChannelsList({ rows = 4 }: { rows?: number }) {
return (
diff --git a/src/frontend/src/pages/Settings.tsx b/src/frontend/src/pages/Settings.tsx
index fc0d1a5..fa763de 100644
--- a/src/frontend/src/pages/Settings.tsx
+++ b/src/frontend/src/pages/Settings.tsx
@@ -25,6 +25,7 @@ import { Modal } from '../components/Modal';
import { FormatProfileForm, type FormatProfileFormValues } from '../components/FormatProfileForm';
import { PlatformSettingsForm, type PlatformSettingsFormValues } from '../components/PlatformSettingsForm';
import { NotificationForm, type NotificationFormValues } from '../components/NotificationForm';
+import { SkeletonSettings } from '../components/Skeleton';
import type { FormatProfile, PlatformSettings } from '@shared/types/index';
// ── Badge styles ──
@@ -40,17 +41,6 @@ const badgeBase: React.CSSProperties = {
letterSpacing: '0.04em',
};
-const iconButtonBase: React.CSSProperties = {
- display: 'inline-flex',
- alignItems: 'center',
- justifyContent: 'center',
- width: 28,
- height: 28,
- borderRadius: 'var(--radius-sm)',
- color: 'var(--text-muted)',
- transition: 'color var(--transition-fast), background-color var(--transition-fast)',
-};
-
// ── Component ──
export function SettingsPage() {
@@ -297,15 +287,7 @@ export function SettingsPage() {
onClick={(e) => { e.stopPropagation(); setEditingPlatform(row.platform); }}
title={`Edit ${row.label} settings`}
aria-label={`Edit ${row.label} settings`}
- style={iconButtonBase}
- onMouseEnter={(e) => {
- e.currentTarget.style.color = 'var(--accent)';
- e.currentTarget.style.backgroundColor = 'var(--accent-subtle)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.color = 'var(--text-muted)';
- e.currentTarget.style.backgroundColor = 'transparent';
- }}
+ className="btn-icon btn-icon-edit"
>
@@ -479,15 +461,7 @@ export function SettingsPage() {
onClick={(e) => { e.stopPropagation(); setEditingProfile(p); }}
title="Edit profile"
aria-label={`Edit ${p.name}`}
- style={iconButtonBase}
- onMouseEnter={(e) => {
- e.currentTarget.style.color = 'var(--accent)';
- e.currentTarget.style.backgroundColor = 'var(--accent-subtle)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.color = 'var(--text-muted)';
- e.currentTarget.style.backgroundColor = 'transparent';
- }}
+ className="btn-icon btn-icon-edit"
>
@@ -496,15 +470,7 @@ export function SettingsPage() {
onClick={(e) => { e.stopPropagation(); setDeletingProfile(p); }}
title="Delete profile"
aria-label={`Delete ${p.name}`}
- style={iconButtonBase}
- onMouseEnter={(e) => {
- e.currentTarget.style.color = 'var(--danger)';
- e.currentTarget.style.backgroundColor = 'var(--danger-bg)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.color = 'var(--text-muted)';
- e.currentTarget.style.backgroundColor = 'transparent';
- }}
+ className="btn-icon btn-icon-delete"
>
@@ -587,18 +553,8 @@ export function SettingsPage() {
title="Send test notification"
aria-label={`Test ${n.name}`}
disabled={result === 'loading'}
- style={{
- ...iconButtonBase,
- opacity: result === 'loading' ? 0.5 : 1,
- }}
- onMouseEnter={(e) => {
- e.currentTarget.style.color = 'var(--success)';
- e.currentTarget.style.backgroundColor = 'var(--success-bg)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.color = 'var(--text-muted)';
- e.currentTarget.style.backgroundColor = 'transparent';
- }}
+ className="btn-icon btn-icon-test"
+ style={{ opacity: result === 'loading' ? 0.5 : 1 }}
>
{result === 'loading'
?
@@ -611,15 +567,7 @@ export function SettingsPage() {
onClick={(e) => { e.stopPropagation(); setEditingNotification(n); }}
title="Edit channel"
aria-label={`Edit ${n.name}`}
- style={iconButtonBase}
- onMouseEnter={(e) => {
- e.currentTarget.style.color = 'var(--accent)';
- e.currentTarget.style.backgroundColor = 'var(--accent-subtle)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.color = 'var(--text-muted)';
- e.currentTarget.style.backgroundColor = 'transparent';
- }}
+ className="btn-icon btn-icon-edit"
>
@@ -629,15 +577,7 @@ export function SettingsPage() {
onClick={(e) => { e.stopPropagation(); setDeletingNotification(n); }}
title="Delete channel"
aria-label={`Delete ${n.name}`}
- style={iconButtonBase}
- onMouseEnter={(e) => {
- e.currentTarget.style.color = 'var(--danger)';
- e.currentTarget.style.backgroundColor = 'var(--danger-bg)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.color = 'var(--text-muted)';
- e.currentTarget.style.backgroundColor = 'transparent';
- }}
+ className="btn-icon btn-icon-delete"
>
@@ -652,12 +592,7 @@ export function SettingsPage() {
// ── Loading state ──
if (profilesLoading) {
- return (
-
-
- Loading settings...
-
- );
+ return ;
}
// ── Error state ──
@@ -683,17 +618,7 @@ export function SettingsPage() {
@@ -792,22 +709,8 @@ export function SettingsPage() {
onClick={handleCopyApiKey}
title={copySuccess ? 'Copied!' : 'Copy to clipboard'}
aria-label="Copy API key to clipboard"
- style={{
- ...iconButtonBase,
- color: copySuccess ? 'var(--success)' : 'var(--text-muted)',
- }}
- onMouseEnter={(e) => {
- if (!copySuccess) {
- e.currentTarget.style.color = 'var(--accent)';
- e.currentTarget.style.backgroundColor = 'var(--accent-subtle)';
- }
- }}
- onMouseLeave={(e) => {
- if (!copySuccess) {
- e.currentTarget.style.color = 'var(--text-muted)';
- e.currentTarget.style.backgroundColor = 'transparent';
- }
- }}
+ className="btn-icon btn-icon-edit"
+ style={copySuccess ? { color: 'var(--success)' } : undefined}
>
{copySuccess ? : }
@@ -817,15 +720,7 @@ export function SettingsPage() {
onClick={() => setShowRegenerateConfirm(true)}
title="Regenerate API key"
aria-label="Regenerate API key"
- style={iconButtonBase}
- onMouseEnter={(e) => {
- e.currentTarget.style.color = 'var(--warning)';
- e.currentTarget.style.backgroundColor = 'var(--warning-bg)';
- }}
- onMouseLeave={(e) => {
- e.currentTarget.style.color = 'var(--text-muted)';
- e.currentTarget.style.backgroundColor = 'transparent';
- }}
+ className="btn-icon btn-icon-warning"
>
@@ -911,17 +806,10 @@ export function SettingsPage() {