True up some UX inconsistencies and have a theme preview pane

This commit is contained in:
Jack Kingsman
2026-03-11 17:03:43 -07:00
parent ad7028e508
commit e993009782
5 changed files with 84 additions and 4 deletions

View File

@@ -515,7 +515,7 @@ function SectionLabel({ children }: { children: React.ReactNode }) {
function ChannelAttributionWarning({
includeAliasNote = false,
nameOnly = false,
className = 'mx-5 my-3 px-3 py-2 rounded-md bg-yellow-500/10 border border-yellow-500/20',
className = 'mx-5 my-3 px-3 py-2 rounded-md bg-warning/10 border border-warning/20',
}: {
includeAliasNote?: boolean;
nameOnly?: boolean;
@@ -523,7 +523,7 @@ function ChannelAttributionWarning({
}) {
return (
<div className={className}>
<p className="text-xs text-yellow-600 dark:text-yellow-400">
<p className="text-xs text-warning">
Channel sender identity is based on best-effort name matching. Different nodes using the
same name will be attributed to the same {nameOnly ? 'sender name' : 'contact'}. Stats below
may be inaccurate.

View File

@@ -40,6 +40,7 @@ export function SettingsLocalSection({
<div className="space-y-1">
<Label>Color Scheme</Label>
<ThemeSelector />
<ThemePreview className="mt-6" />
</div>
<Separator />
@@ -91,3 +92,64 @@ export function SettingsLocalSection({
</div>
);
}
function ThemePreview({ className }: { className?: string }) {
return (
<div className={`rounded-lg border border-border bg-card p-3 ${className ?? ''}`}>
<p className="text-xs text-muted-foreground mb-3">
Preview alert and message contrast for the selected theme.
</p>
<div className="space-y-2">
<PreviewBanner className="border border-status-connected/30 bg-status-connected/15 text-status-connected">
Connected preview: radio link healthy and syncing.
</PreviewBanner>
<PreviewBanner className="border border-warning/50 bg-warning/10 text-warning">
Warning preview: packet audit suggests missing history.
</PreviewBanner>
<PreviewBanner className="border border-destructive/30 bg-destructive/10 text-destructive">
Error preview: radio reconnect failed.
</PreviewBanner>
</div>
<div className="mt-4 space-y-2">
<PreviewMessage
sender="Alice"
bubbleClassName="bg-msg-incoming text-foreground"
text="Hello, mesh!"
/>
<PreviewMessage
sender="You"
alignRight
bubbleClassName="bg-msg-outgoing text-foreground"
text="Hi there! I'm using RemoteTerm."
/>
</div>
</div>
);
}
function PreviewBanner({ children, className }: { children: React.ReactNode; className: string }) {
return <div className={`rounded-md px-3 py-2 text-xs ${className}`}>{children}</div>;
}
function PreviewMessage({
sender,
text,
bubbleClassName,
alignRight = false,
}: {
sender: string;
text: string;
bubbleClassName: string;
alignRight?: boolean;
}) {
return (
<div className={`flex ${alignRight ? 'justify-end' : 'justify-start'}`}>
<div className={`max-w-[85%] ${alignRight ? 'items-end' : 'items-start'} flex flex-col`}>
<span className="mb-1 text-[11px] text-muted-foreground">{sender}</span>
<div className={`rounded-2xl px-3 py-2 text-sm break-words ${bubbleClassName}`}>{text}</div>
</div>
</div>
);
}

View File

@@ -460,7 +460,7 @@ export function SettingsRadioSection({
<option value="1">2 bytes</option>
<option value="2">3 bytes</option>
</select>
<div className="rounded-md border border-amber-500/50 bg-amber-500/10 p-3 text-xs text-amber-200">
<div className="rounded-md border border-warning/50 bg-warning/10 p-3 text-xs text-warning">
<p className="font-semibold mb-1">Compatibility Warning</p>
<p>
ALL nodes along a message&apos;s route &mdash; your radio, every repeater, and the

View File

@@ -298,7 +298,7 @@ export function VisualizerControls({
</button>
<button
onClick={onClearAndReset}
className="mt-1 px-3 py-1.5 bg-yellow-500/20 hover:bg-yellow-500/30 text-yellow-500 rounded text-xs transition-colors"
className="mt-1 rounded border border-warning/40 bg-warning/10 px-3 py-1.5 text-warning text-xs transition-colors hover:bg-warning/20"
title="Clear all nodes and links from the visualization - packets are preserved"
>
Clear &amp; Reset

View File

@@ -262,6 +262,24 @@ describe('SettingsModal', () => {
expect(screen.queryByLabelText('Local label text')).not.toBeInTheDocument();
});
it('shows the theme contrast preview in local settings', () => {
renderModal();
openLocalSection();
expect(
screen.getByText('Preview alert and message contrast for the selected theme.')
).toBeInTheDocument();
expect(
screen.getByText('Connected preview: radio link healthy and syncing.')
).toBeInTheDocument();
expect(
screen.getByText('Warning preview: packet audit suggests missing history.')
).toBeInTheDocument();
expect(screen.getByText('Error preview: radio reconnect failed.')).toBeInTheDocument();
expect(screen.getByText('Hello, mesh!')).toBeInTheDocument();
expect(screen.getByText("Hi there! I'm using RemoteTerm.")).toBeInTheDocument();
});
it('clears stale errors when switching external desktop sections', async () => {
const onSaveAppSettings = vi.fn(async () => {
throw new Error('Save failed');