feat: Themes added, 4 themes available (3 dark, 1 light)

This commit is contained in:
2026-07-06 09:08:47 +02:00
parent fafa0c22ab
commit 06183bd5d4
43 changed files with 982 additions and 457 deletions
+15 -15
View File
@@ -79,7 +79,7 @@ function Slider({ value, onChange, disabled, accent = '#16a34a', step = 1, max =
onChange={(e) => onChange(parseInt(e.target.value, 10))}
className={cn('flex-1 h-1.5 rounded-full appearance-none cursor-pointer disabled:opacity-30 disabled:cursor-default',
'[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-3.5 [&::-webkit-slider-thumb]:rounded-full',
'[&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:shadow-sm [&::-webkit-slider-thumb]:cursor-pointer')}
'[&::-webkit-slider-thumb]:bg-card [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:shadow-sm [&::-webkit-slider-thumb]:cursor-pointer')}
style={{ background: `linear-gradient(to right, ${accent} ${pct}%, #d8cfb8 ${pct}%)`, borderColor: accent }}
/>
);
@@ -107,10 +107,10 @@ function Chip({ on, onClick, label, disabled, accent = 'emerald' }: {
on: boolean; onClick: () => void; label: string; disabled?: boolean; accent?: 'emerald' | 'violet' | 'cyan' | 'amber';
}) {
const onCls = {
emerald: 'bg-emerald-600 border-emerald-600 text-white',
violet: 'bg-violet-600 border-violet-600 text-white',
cyan: 'bg-cyan-600 border-cyan-600 text-white',
amber: 'bg-amber-500 border-amber-500 text-white',
emerald: 'bg-success border-success text-success-foreground',
violet: 'bg-info border-info text-info-foreground',
cyan: 'bg-info border-info text-info-foreground',
amber: 'bg-warning border-warning text-warning-foreground',
}[accent];
return (
<button type="button" onClick={onClick} disabled={disabled}
@@ -273,8 +273,8 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
</div>
<div className="flex-1" />
<span className={cn('inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-extrabold tracking-wider',
!st.available ? 'bg-slate-700 text-slate-300'
: st.transmitting ? 'bg-rose-500 text-white shadow-[0_0_16px] shadow-rose-500/60' : 'bg-emerald-500 text-white')}>
!st.available ? 'bg-muted text-muted-foreground'
: st.transmitting ? 'bg-danger text-danger-foreground shadow-[0_0_16px] shadow-danger/60' : 'bg-success text-success-foreground')}>
<span className="size-2 rounded-full bg-current" />
{!st.available ? t('flxp.offline') : st.transmitting ? 'TX' : 'RX'}
</span>
@@ -304,13 +304,13 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
<button type="button" disabled={off}
onClick={() => change('tune', !st.tune, () => FlexTune(!st.tune))}
className={cn('flex-1 px-3 py-2.5 rounded-lg text-sm font-extrabold tracking-wide border-2 transition-all disabled:opacity-30',
st.tune ? 'bg-amber-500 text-white border-amber-500 shadow-[0_0_14px] shadow-amber-500/50' : 'bg-card text-amber-700 border-amber-400 hover:bg-amber-50')}>
st.tune ? 'bg-warning text-warning-foreground border-warning shadow-[0_0_14px] shadow-warning/50' : 'bg-card text-warning border-warning hover:bg-warning-muted')}>
TUNE
</button>
<button type="button" disabled={off}
onClick={() => change('transmitting', !st.transmitting, () => FlexMox(!st.transmitting))}
className={cn('flex-1 px-3 py-2.5 rounded-lg text-sm font-extrabold tracking-wide border-2 transition-all disabled:opacity-30',
st.transmitting ? 'bg-rose-600 text-white border-rose-600 shadow-[0_0_14px] shadow-rose-600/50' : 'bg-card text-rose-700 border-rose-400 hover:bg-rose-50')}>
st.transmitting ? 'bg-danger text-danger-foreground border-danger shadow-[0_0_14px] shadow-danger/50' : 'bg-card text-danger border-danger hover:bg-danger-muted')}>
<Power className="size-4 inline mr-1 -mt-0.5" /> MOX
</button>
</div>
@@ -319,7 +319,7 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
title={t('flxp.splitHint')}
onClick={() => change('split', !st.split, () => FlexSetSplit(!st.split))}
className={cn('px-3 py-1.5 rounded-lg text-sm font-extrabold tracking-wide border-2 transition-all disabled:opacity-30',
st.split ? 'bg-sky-600 text-white border-sky-600 shadow-[0_0_12px] shadow-sky-600/50' : 'bg-card text-sky-700 border-sky-400 hover:bg-sky-50')}>
st.split ? 'bg-info text-info-foreground border-info shadow-[0_0_12px] shadow-info/50' : 'bg-card text-info border-info hover:bg-info-muted')}>
SPLIT
</button>
{st.split && !!st.tx_freq_hz && (
@@ -414,7 +414,7 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
title={st.mute ? t('flxp.muted') : t('flxp.mute')}
onClick={() => change('mute', !st.mute, () => FlexSetMute(!st.mute))}
className={cn('shrink-0 rounded p-1 transition-colors disabled:opacity-30',
st.mute ? 'bg-red-600 text-white' : 'text-muted-foreground hover:bg-muted')}>
st.mute ? 'bg-destructive text-destructive-foreground' : 'text-muted-foreground hover:bg-muted')}>
{st.mute ? <VolumeX className="size-3.5" /> : <Volume2 className="size-3.5" />}
</button>
<Slider value={st.audio_level} disabled={rxOff || st.mute} accent="#16a34a" onChange={(v) => change('audio_level', v, () => FlexSetAudioLevel(v))} />
@@ -495,7 +495,7 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
<button type="button" disabled={off}
onClick={() => change('amp_operate', !st.amp_operate, () => FlexAmpOperate(!st.amp_operate))}
className={cn('px-4 py-2 rounded-lg text-sm font-extrabold tracking-wide border-2 transition-all disabled:opacity-30',
st.amp_operate ? 'bg-orange-600 text-white border-orange-600 shadow-[0_0_14px] shadow-orange-600/50' : 'bg-card text-orange-700 border-orange-400 hover:bg-orange-50')}>
st.amp_operate ? 'bg-warning text-warning-foreground border-warning shadow-[0_0_14px] shadow-warning/50' : 'bg-card text-warning border-warning hover:bg-warning-muted')}>
{st.amp_operate ? 'OPERATE' : 'STANDBY'}
</button>
<span className="text-xs text-muted-foreground">
@@ -506,13 +506,13 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
selector is disabled until connected (hover it for the error). */}
{(pg.host || pg.connected) && (
<label className="flex items-center gap-1.5 text-xs" title={pg.connected ? t('flxp.pgConnected') : (pg.last_error || t('flxp.pgOffline'))}>
<span className={cn('size-1.5 rounded-full', pg.connected ? 'bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.8)]' : 'bg-rose-500')} />
<span className={cn('size-1.5 rounded-full', pg.connected ? 'bg-success shadow-[0_0_6px_rgba(16,185,129,0.8)]' : 'bg-danger')} />
<span className="text-muted-foreground">{t('flxp.fan')}</span>
<select
disabled={!pg.connected}
value={(pg.fan_mode || 'CONTEST').toUpperCase()}
onChange={(e) => { const v = e.target.value; setPg((s) => ({ ...s, fan_mode: v })); PGXLSetFanMode(v).catch(() => {}); }}
className="h-8 rounded-md border border-orange-300 bg-card px-2 text-xs font-semibold text-orange-800 outline-none focus:border-orange-500 disabled:opacity-40"
className="h-8 rounded-md border border-warning-border bg-card px-2 text-xs font-semibold text-warning outline-none focus:border-warning disabled:opacity-40"
>
<option value="STANDARD">{t('flxp.fanStandard')}</option>
<option value="CONTEST">{t('flxp.fanContest')}</option>
@@ -522,7 +522,7 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
)}
<div className="flex-1" />
{st.amp_fault && st.amp_fault !== 'NONE' && (
<span className="px-2 py-1 rounded bg-rose-100 text-rose-800 text-xs font-bold">{t('flxp.fault')}: {st.amp_fault}</span>
<span className="px-2 py-1 rounded bg-danger-muted text-danger-muted-foreground text-xs font-bold">{t('flxp.fault')}: {st.amp_fault}</span>
)}
</div>
</Card>