mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
True up some UX inconsistencies and have a theme preview pane
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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's route — your radio, every repeater, and the
|
||||
|
||||
@@ -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 & Reset
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user